/**
 * @class Spark
 * Spark 核心类.
 * @author 牛佳庆
 * @singleton
 */
$S = Spark = {version: '1.0'};
$S.fireWindowResize = function(){
    $S.Cover.resizeCover();
}
Ext.EventManager.on(window, "resize", $S.fireWindowResize, this);
Ext.EventManager.on(document, "mousewheel", function(e){
    if(e.ctrlKey)e.preventDefault();
    //Where is the "Ctrl + mouse wheel" page size adjustment reset button?  ctrl + 0 
}, this);


$S.cache = {};
$S.cmps = {};
$S.onReady = function(fn, scope, options){
    if(window['__host']){
        if(!$S.loadEvent)$S.loadEvent = new Ext.util.Event();
        $S.loadEvent.addListener(fn, scope, options);
    }else{
        Ext.onReady(fn, scope, options);
    }
}

$S.get = Ext.get;
$S.defaultDateFormat='isoDate';
$S.defaultDateTimeFormat="yyyy-mm-dd HH:MM:ss";
$S.defaultChineseLength = 2;

$S.viewHeight = 768;
$S.viewWidth = 1024;
$S.suk = null;



$S.system = function(){
    var l = arguments.length;
    if(l==1){
        if($S._system){
            return $S._system[arguments[0]]
        }
    }else if(l==2){
        if(!$S.system) $S._system = {};
        $S._system[arguments[0]] = arguments[1]
    }
}
$S.center = function(el){
    var ele;
    if(typeof(el)=="string"){
        var cmp = $S.CmpManager.get(el)
        if(cmp){
            if(cmp.wrap){
                ele = cmp.wrap;
            }
        }else{
             ele = Ext.get(el);
        }             
    }else{
        ele = Ext.get(el);
    }
    var screenWidth = $S.getViewportWidth();
    var screenHeight = $S.getViewportHeight();
    var x = Math.max(0,(screenWidth - ele.getWidth())/2);
    var y = Math.max(0,(screenHeight - ele.getHeight())/2);
    ele.setStyle('position','absolute');
    ele.moveTo(x,y);
}

$S.CmpManager = function(){
    return {
        put : function(id, cmp){
            if(window['__host']&&window['__host'].cmps)window['__host'].cmps[id] = cmp;
            if(!this.cache) this.cache = {};
            if(this.cache[id] != null) {
                alert("Error: Componet '" + id +"' already exists!");
                return;
            }
            this.cache[id]=cmp;
//            cmp.on('mouseover',$S.CmpManager.onCmpOver,$S.CmpManager);
//            cmp.on('mouseout',$S.CmpManager.onCmpOut,$S.CmpManager);
        },
//        onCmpOver: function(cmp, e){
//            if(cmp.binder){
//                var ds = cmp.binder.ds;
//                if(!ds)return;
//                var record = cmp.record;
//                if(!record)return;
//                var field = record.getField(cmp.binder.name)
//                var msg = record.valid[cmp.binder.name] || field.get('tooltip');                
//                if(Ext.isEmpty(msg))return;
//                $S.ToolTip.show(cmp.wrap, msg);
//            }
//        },
//        onCmpOut: function(cmp,e){
//            if(cmp.binder)
//            $S.ToolTip.hide();
//        },
        getAll : function(){
            return this.cache;
        },
        remove : function(id){
            var cmp = this.cache[id];
            if(cmp) {
                if(cmp['__host'] && cmp['__host'].cmps){
                    delete cmp['__host'].cmps[id];        
                }
                cmp.un('mouseover',$S.CmpManager.onCmpOver,$S.CmpManager);
                cmp.un('mouseout',$S.CmpManager.onCmpOut,$S.CmpManager);
                delete this.cache[id];
            }
        },
        get : function(id){
            if(!this.cache) return null;
            return this.cache[id];
        }
    };
}();
Ext.Ajax.on("requestexception", function(conn, response, options) {
    if(options.longpolling) return;
    $S.EM.fireEvent('ajaxerror', $S.EM, response.status, response);
    if(response.isTimeout) {
        $S.showErrorMessage('错误', "网络超时!");
        return;
    }
    switch(response.status){
        case 404:
            $S.showErrorMessage('404 Error', response.statusText,null,400,150);
            break;
        case 500:
            $S.showErrorMessage(response.status + ' Error', response.responseText,null,800,600);
            break;
        default:
            $S.showErrorMessage('Error', response.statusText);
            break;
    }   
}, this);
$ = $S.getCmp = function(id){
    var cmp = $S.CmpManager.get(id)
    return cmp;
}
$S.equals = function(v1,v2){
    if(v1 === v2 || (Ext.isEmpty(v1,false)&&Ext.isEmpty(v2,false)))return true;
    if(Ext.isDate(v1) && Ext.isDate(v2)){
        if(v1.xtype=='datetime' && v2.xtype=='datetime') {
            return v1.getTime() == v2.getTime();
        }else{
            return v1.getFullYear()==v2.getFullYear() && v1.getMonth() == v2.getMonth() && v1.getDate() == v2.getDate();        
        }
    }
    return false;
}
$S.setCookie = function(name,value){
    var pathname = location.pathname;
    pathname = pathname.substring(0, pathname.lastIndexOf('/'));
    document.cookie = name + "="+ escape (value) +';path = ' + pathname;
}
$S.getCookie = function(name){
    var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));
    if(arr != null) return unescape(arr[2]); return null;

}
$S.getViewportHeight = function(){
    if(Ext.isIE){
        return Ext.isStrict ? document.documentElement.clientHeight :
                 document.body.clientHeight;
    }else{
        return self.innerHeight;
    }
}
$S.getViewportWidth = function() {
    if(Ext.isIE){
        return Ext.isStrict ? document.documentElement.clientWidth :
                 document.body.clientWidth;
    }else{
        return self.innerWidth;
    }
}
$S.post = function(action,data){
    var form = Ext.getBody().createChild({tag:'form',method:'post',action:action});
    for(var key in data){
        if(data[key])
        form.createChild({tag:"input",type:"hidden",name:key,value:data[key]});
    }
    form.dom.submit();
}
$S.request = function(opt){
    var url = opt.url,para = opt.para,successCall = opt.success,errorCall = opt.error,scope = opt.scope,failureCall = opt.failure;
    var opts = Ext.apply({},opt.opts);
    $S.EM.fireEvent('ajaxstart', url, para);
    return Ext.Ajax.request({
        url: url,
        method: 'POST',
        params:{SUK:$S.suk,_sph:$S.viewHeight,_spw:$S.viewWidth,_request_data:Ext.util.JSON.encode({parameter:para})},
        opts:opts,
        success: function(response,options){
            $S.EM.fireEvent('ajaxcomplete', url, para,response);
            if(response){
                var res = null;
                try {
                    res = Ext.decode(response.responseText);
                }catch(e){}
                if(res){
                    if(res.success){//成功
                        $S.EM.fireEvent('ajaxsuccess', $S.EM, url,para,res);
                        if(successCall)successCall.call(scope,res, options);
                    }else{//失败
                        $S.EM.fireEvent('ajaxfailed', $S.EM, url,para,res);
                        var w = res.width||400,h = res.height||250;
                        var msg = res.message||'';
                        if(res.failedType == 'error'){
                            $S.showErrorMessage(res.title||'错误', msg,null,w,h);
                        }else if(res.failedType == 'warning'){
                            $S.showWarningMessage(res.title||'警告', msg,null,w,h);
                        }else if(res.failedType == 'info'){
                            $S.showInfoMessage(res.title||'提示', msg,null,w,h);
                        }else if(res.failedType == 'sessionTimeout'){
                            $S.EM.fireEvent('sessionTimeout');
                        }
                        if(errorCall)errorCall.call(scope, res, options);  
                    }
                }else{
                    $S.showErrorMessage('系统错误', '返回格式不正确!');
                    if(errorCall)errorCall.call(scope, null, options);
                }
//              if(res && !res.success){
//                  $S.EM.fireEvent('ajaxfailed', $S.EM, url,para,res);
//                  if(res.error){
//                      var st = res.error.stackTrace;
//                      st = (st) ? st.replaceAll('\r\n','</br>') : '';
//                      if(res.error.message) {
//                          var h = (st=='') ? 150 : 250;
//                          $S.showErrorMessage('Error', res.error.message+'</br>'+st,null,400,h);
//                      }else{
//                          $S.showErrorMessage('Error', st,null,400,250);
//                      }
//                      if(errorCall)
//                        errorCall.call(scope, res, options);  
//                  }                                                               
//              } else {
//                  $S.EM.fireEvent('ajaxsuccess', $S.EM, url,para,res);
//                  if(successCall)successCall.call(scope,res, options);
//              }
            }
        },
        failure : function(response, options){
            if(failureCall)failureCall.call(scope, response, options);
        },
        scope: scope
    });
}

$S.dateFormat = function () { 
    var masks = {  
        "default":      "ddd mmm dd yyyy HH:MM:ss",  
        shortDate:      "m/d/yy",  
        mediumDate:     "mmm d, yyyy",  
        longDate:       "mmmm d, yyyy",  
        fullDate:       "dddd, mmmm d, yyyy",  
        shortTime:      "h:MM TT",  
        mediumTime:     "h:MM:ss TT",  
        longTime:       "h:MM:ss TT Z",  
        isoDate:        "yyyy-mm-dd",  
        isoTime:        "HH:MM:ss",  
        isoDateTime:    "yyyy-mm-dd HH:MM:ss", // "yyyy-mm-dd'T'HH:MM:ss",  
        isoUtcDateTime: "UTC:yyyy-mm-dd HH:MM:ss'Z'"  //"UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" 
    };
    var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,  
        timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,  
        timezoneClip = /[^-+\dA-Z]/g,  
        pad = function (val, len) {  
            val = String(val);  
            len = len || 2;  
            while (val.length < len) val = "0" + val;  
            return val;  
        },
        hasTimeStamp = function(mask,token){
            return !!String(masks[mask] || mask || masks["default"]).match(token);
        },
        _parseDate=function(s,mask,fun){
            if(!s)return s;
            for(var i=0,arr=mask.match(token),numbers=s.match(/\d+/g),value;i<arr.length;i++){
                if(numbers.length==arr.length)value=numbers[i];
                else value=parseInt(s.slice(index=mask.search(arr[i]),index+arr[i].length));
                switch(arr[i]){
                    case "mm":;
                    case "m":value=value-1;break;
                }
                fun(arr[i],value);
            }
        }; 
    return {
        pad:pad,
        parseDate:function(string,mask,utc){
            if(typeof string!="string"||string=="")return null;
            mask = String(masks[mask] || mask || masks["default"]); 
            if (mask.slice(0, 4) == "UTC:") {  
                mask = mask.slice(4);  
                utc = true;  
            }
            var date=new Date(1970,1,2,0,0,0),
                _ = utc ? "setUTC" : "set",  
                d = date[_ + "Date"],  
                m = date[_ + "Month"],  
                yy = date[_ + "FullYear"], 
                y = date[_ + "Year"], 
                H = date[_ + "Hours"],  
                M = date[_ + "Minutes"],  
                s = date[_ + "Seconds"],  
                L = date[_ + "Milliseconds"],  
                //o = utc ? 0 : date.getTimezoneOffset();
                flags = {  
                    d:    d,  
                    dd:   d,
                    m:    m,  
                    mm:   m,  
                    yy:   y,  
                    yyyy: yy,  
                    h:    H,  
                    hh:   H,  
                    H:    H,  
                    HH:   H,  
                    M:    M,  
                    MM:   M,  
                    s:    s,  
                    ss:   s,  
                    l:    L,  
                    L:    L
                }; 
                try{
                    _parseDate(string,mask,function($0,value){
                        if(flags[$0])flags[$0].call(date,value);
                    });
                }catch(e){throw new SyntaxError("invalid date");}
                if (isNaN(date)) throw new SyntaxError("invalid date"); 
                return date;
        },
        format:function (date, mask, utc) {    
            if (arguments.length == 1 && (typeof date == "string" || date instanceof String) && !/\d/.test(date)) {  
                mask = date;  
                date = undefined;  
            }   
            date = date ? new Date(date) : new Date();  
            if (isNaN(date)) throw new SyntaxError("invalid date");  
      
            mask = String(masks[mask] || mask || masks["default"]);  
            if (mask.slice(0, 4) == "UTC:") {  
                mask = mask.slice(4);  
                utc = true;  
            }  
      
            var _ = utc ? "getUTC" : "get",  
                d = date[_ + "Date"](),  
                D = date[_ + "Day"](),  
                m = date[_ + "Month"](),  
                y = date[_ + "FullYear"](),  
                H = date[_ + "Hours"](),  
                M = date[_ + "Minutes"](),  
                s = date[_ + "Seconds"](),  
                L = date[_ + "Milliseconds"](),  
                o = utc ? 0 : date.getTimezoneOffset(),  
                flags = {  
                    d:    d,  
                    dd:   pad(d),
                    m:    m + 1,  
                    mm:   pad(m + 1),  
                    yy:   String(y).slice(2),  
                    yyyy: y,  
                    h:    H % 12 || 12,  
                    hh:   pad(H % 12 || 12),  
                    H:    H,  
                    HH:   pad(H),  
                    M:    M,  
                    MM:   pad(M),  
                    s:    s,  
                    ss:   pad(s),  
                    l:    pad(L, 3),  
                    L:    pad(L > 99 ? Math.round(L / 10) : L),  
                    t:    H < 12 ? "a"  : "p",  
                    tt:   H < 12 ? "am" : "pm",  
                    T:    H < 12 ? "A"  : "P",  
                    TT:   H < 12 ? "AM" : "PM",  
                    Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),  
                    o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),  
                    S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]  
                }; 
            return mask.replace(token, function ($0) {  
                return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);  
            });  
        },
        isDateTime:function(mask){
            return hasTimeStamp(mask,/([HhMs])\1?/);
        }
    };  
}();

Ext.applyIf(String.prototype, {
    trim : function(){
        return this.replace(/(^\s*)|(\s*$)/g, "");
    },
    replaceAll : function(s1,s2){
        return this.replace(new RegExp(s1,"gm"),s2);  
    },
    parseDate : function(mask,utc){
        return $S.dateFormat.parseDate(this.toString(),mask,utc);  
    }
});
Ext.applyIf(Date.prototype, {
    format : function(mask, utc){
        return $S.dateFormat.format(this, mask, utc);  
    },
    isSame : function(d){
        if(!this.getFullYear||!d.getFullYear)return false;
        return (this.getFullYear() == d.getFullYear() && this.getMonth() == d.getMonth() && this.getDate() == d.getDate());
    }
});
Ext.applyIf(Array.prototype, {
    add : function(o){
        if(this.indexOf(o) == -1)
        this[this.length] = o;
    },
    find : function(n,v){
        for(var i = 0, len = this.length; i < len; i++){
            var o = this[i];
            if(o[n] == v) return o
        }
        return null;
    }
});
$S.TextMetrics = function(){
    return {
        measure : function(text){
            var shared = $S.TextMetrics.Instance();
            return shared.getSize(text);
        }
    };
}();
$S.TextMetrics.Instance = function(){
    var p = '<div style="left:-10000px;top:-10000px;position:absolute;visibility:hidden"></div>';
    if(!$S.TextMetrics.obj) {
        $S.TextMetrics.obj = Ext.DomHelper.append(Ext.get(document.body),p)
    }
    var ml = Ext.get($S.TextMetrics.obj);
    var instance = {      
        getSize : function(text){
            ml.update(text);            
            var s=new Object();
            s.width=ml.getWidth();
            s.height=ml.getHeight();
            ml.update('');
            return s;
        },       
        bind : function(el){
            var a=new Array('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing');  
            var len = a.length, r = {};
            for(var i = 0; i < len; i++){
                r[a[i]] = Ext.fly(el).getStyle(a[i]);
            }
            ml.setStyle(r);           
        },       
        setFixedWidth : function(width){
            ml.setWidth(width);
        }       
    };
    instance.bind(document.body);
    return instance;
};

$S.DocMg = function(){
    var me = {
        handlers : [],
        init: function(){
            Ext.each(this.handlers,function(h){
                if(h.init) h.init();
            },this)
            Ext.get(document.documentElement).on("mouseover", this.onDocMouseOver, this);
            Ext.get(document.documentElement).on("mousedown", this.onDocMouseDown, this);
        },
        onDocMouseOver : function(e){
            Ext.each(this.handlers,function(h){
                if(h.onMouseOver) h.onMouseOver(e);
            },this)
        },
        onDocMouseDown : function(e){
            Ext.each(this.handlers,function(h){
                if(h.onMouseDown) h.onMouseDown(e);
            },this)
        },
        register : function(h){
            this.handlers.push(h);
        }
    }
    Ext.onReady(function(){
        me.init();
    });
    return me;
}();
//$S.Tip = function(){
//    var tip = {
//        el:null,
//        styles:{},
////        fa: new Array('height','line-height'), 
//        fa: new Array('text-overflow','overflow', 'position', 'z-index', 'max-width','width','height','line-height'), 
//        hide: function(){;
//            if(this.el){
//                this.el.removeClass('s-tip');
//                //this.el.parent().setStyle({'position':'static'});
//                this.el.setStyle(this.styles);
//                this.styles = {};
////                this.el.setStyle({
////                    'text-overflow' : 'ellipsis',
////                    'overflow':'hidden',
////                    'position': 'absolute',
////                    'z-index':''
////                });
//                this.el = null;
//            }
//        },
//        show: function(t){
//            this.el = t;
//            var isTip = Ext.fly(t).getAttributeNS('','tip');
//            if(isTip == '') return;
//            var r={},text = t.dom.innerHTML,ew = t.getWidth(),tw = $S.TextMetrics.measure(text),dbody = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'], sh=$S.getViewportHeight(),sw=$S.getViewportWidth(),
//                sl = dbody.scrollLeft,st = dbody.scrollTop,tx = t.getX() - sl,ty = t.getY() - st;
//            if(tw.width > ew){
//                for(var i = 0; i < this.fa.length; i++){
//                    this.styles[this.fa[i]] = Ext.fly(t).getStyle(this.fa[i]);
//                }
//                var tb = Ext.fly(t).getBorderWidth('t'),bb = Ext.fly(t).getBorderWidth('b');
//                var h = Ext.fly(t).getHeight()-tb-bb;
//                this.el.addClass('s-tip');
//                this.el.setStyle({
//                    'text-overflow' : 'clip',
//                    'overflow':'visible',
//                    'position': 'absolute',
//                    'line-height':h+'px',
//                    'height':h+'px',
//                    'width':'auto',
//                    'max-width' : 'none',
//                    'z-index':99999
//                });
//                //this.el.parent().setStyle({'position':'relative'})
//                this.el.setX(tx-1);
//                this.el.setY(ty-1);
//            }
//        },
//        findTarget: function(e){
//            var tg = Ext.get(e.target);
//            var t = tg.getAttributeNS('','tip');
//            if(t=='true'){ 
//                return tg;
//            }
//        },
//        onMouseDown: function(e){
//            this.hide();
//        },
//        onMouseOver: function(e){
//            e.stopEvent();
//            var sf = this;
//            if(sf.el && sf.el.contains(e.target)) {
//                return;
//            }
//            var tg = sf.findTarget(e);
//            sf.hide();
//            if(tg) sf.show(tg);
//        }
//    }
//    $S.DocMg.register(tip);
//    return tip
//}();
$S.Tip = function(){
    var tip = {
        el:null, 
        tpl:'<div class="s-tip" style=""background-color:#ededed;Z-INDEX: 99999; POSITION:absolute; left:0px;top:0px;white-space:nowrap;LINE-HEIGHT: 30px; WIDTH: auto; TEXT-OVERFLOW: clip; MAX-WIDTH: none; HEIGHT: 30px; OVERFLOW: visible;"></div>',
        fa: new Array('font-size','text-indent', 'font-style', 'font-weight', 'font-family','line-height', 'letter-spacing','padding-right'),  
        init: function(){
            var p = '<div class="s-tip"></div>';
            this.el = Ext.get(Ext.DomHelper.insertFirst(Ext.getBody(),p));            
        },
        hide: function(){           
            this.el.dom.style.display = 'none';
            var p = this.el.parent('.grid-cell');
            if(p) p.removeClass('s-tip-parent');
            Ext.getBody().appendChild(this.el);
        },
        show: function(t){
            var isTip = Ext.fly(t).getAttributeNS('','tip');
            if(isTip == '') return;
            var pr={}, r={},text = t.dom.innerHTML,ew = t.getWidth(),tw = $S.TextMetrics.measure(text);
            if(tw.width+10 > ew){//text-indent + padding-right = 10
                var dbody = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'], sh=$S.getViewportHeight(),sw=$S.getViewportWidth(),
                sl = dbody.scrollLeft,st = dbody.scrollTop,tx = t.getX() - sl,ty = t.getY() - st;
                this.el.update(text);
                this.el.setHeight(t.getHeight()-1);
                for(var i = 0; i < this.fa.length; i++){
                    r[this.fa[i]] = Ext.fly(t).getStyle(this.fa[i]);
                }
                r['line-height'] = t.getHeight() + 'px';
                var p = this.el.parent('.grid-cell');
                if(p) p.removeClass('s-tip-parent');
                this.el.setStyle(r);
                Ext.fly(t).appendChild(this.el);
                this.el.parent('.grid-cell').addClass('s-tip-parent');
                this.el.dom.style.display = 'block';
            }
        },
        findTarget: function(e){
            var tg = Ext.get(e.target);
            var t = tg.getAttributeNS('','tip');
            return t == '' ? null : tg;
        },
        onMouseDown: function(e){
            this.hide();
        },
        onMouseOver: function(e){
            e.stopEvent();
            var sf = this;
            if(sf.el && sf.el.contains(e.target)) {
                return;
            }
            var tg = sf.findTarget(e);
            sf.hide();
            if(tg){
                sf.show(tg);
            }
        }
    }
    $S.DocMg.register(tip);
    return tip
}();
$S.ToolTip = function(){
    var tip = {
        dt:'data-tooltip',
        hid:null,
        el:null,
        crt:null,
        ecs: ['s-bubble-promo','s-bubble-black','s-bubble-valid'],
        ars : ['s-bubble-arrowdown','s-bubble-arrowup','s-bubble-arrowleft','s-bubble-arrowright'],       
        init: function(){
            var p = '<div class="s-bubble" ><div class="s-bubble-content-id"></div><div class="s-bubble-arrow-id s-bubble-arrow"><div class="s-bubble-arrowimplbefore"></div><div class="s-bubble-arrowimplafter"></div></div></div>';
            this.el = Ext.get(Ext.DomHelper.insertFirst(Ext.getBody(),p));
        },
        hide: function(){
            this.crt = null;
            this.el.moveTo(-10000,-10000);
            this.el.dom.style.opacity = 0;
        },
        getPosition : function(align){
            return {
                isT : align.indexOf('t') == 0,
                isTL : align.indexOf('tl') !== -1,
                isTR : align.indexOf('tr') !== -1,
                isB : align.indexOf('b') == 0,
                isBL : align.indexOf('bl') !== -1,
                isBR : align.indexOf('br') !== -1,
                isL : align.indexOf('l') == 0,
                isLT : align.indexOf('lt') !== -1,
                isLB : align.indexOf('lb') !== -1,
                isR : align.indexOf('r') ==0,
                isRT : align.indexOf('rt') !== -1,
                isRB : align.indexOf('rb') !== -1
            }
        },
        restrict : function(t){
            var type = Ext.fly(t).getAttributeNS('',this.dt + '-type'), margin = type=='tip' ? 2 : 10,tip = this.el,ar = tip.child('div.s-bubble-arrow'),align = Ext.fly(t).getAttributeNS('',this.dt + '-align'),align = align ? align : 'b',
                dbody = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'], sh=$S.getViewportHeight(),sw=$S.getViewportWidth(),
                sl = dbody.scrollLeft,st = dbody.scrollTop,tw = t.getWidth(),th = t.getHeight(),tph = tip.getHeight(),
                tpw = tip.getWidth(),tx = t.getX() - sl,ty = t.getY() - st,ap = this.getPosition(align),p1=align.substring(0,1),p2=align.substring(1,2);
                if(ap.isT||ap.isB) {
                    if(ty - tph - margin <=0){
                        p1 = 'b';
                    }else if(ty + th + tph + margin >sh){
                        p1 = 't';
                    }
                    if((ap.isTR||ap.isBR) && tx + tpw > sw) {
                        p2= 'l';
                    }else if((ap.isTL||ap.isBL) && tx + tpw - tpw <=0) {
                        p2= 'r';
                    }else if(((!ap.isTL && !ap.isTR)||(!ap.isBL && !ap.isBR)) && tx + tw/2 + tpw/2 > sw) {
                        p2= 'l';
                    }else if(((!ap.isTL && !ap.isTR)||(!ap.isBL && !ap.isBR)) && tx + tw/2 - tpw/2 <=0) {
                        p2= 'r';
                    }else if(tpw<tw){
                        p2='';
                    }
                }
                if(ap.isL||ap.isR) {
                    if(tx + tw + tpw + margin > sw) {
                        p1= 'l';
                    } else if(tx - tpw - margin <=0) {
                        p1= 'r';
                    }
                    if((ap.isLT||ap.isRT) && ty + th - tph<=0){
                        p2 = 'b';
                    }else if((ap.isLB||ap.isRB) && ty + tph>sh){
                        p2 = 't';
                    }else if(((!ap.isLT && !ap.isLB)||(!ap.isRT && !ap.isRB)) && ty + th/2 + tph/2 > sh) {
                        p2= 't';
                    }else if(((!ap.isLT && !ap.isLB)||(!ap.isRT && !ap.isRB)) && ty + th/2 - tph/2 <=0) {
                        p2= 'b';
                    }else if(tph<th){
                        p2='';
                    }
                }
                var x,y,at,al,p = p1+p2,arcs,ap2 = this.getPosition(p);
                if(ap2.isT||ap2.isB){
                    arcs = ap2.isT ? this.ars[0] : this.ars[1];
                    if(p=='t'||p=='b'){
                        x = tx + (tw-tpw)/2;
                        al = tpw/2;
                    }else if(ap2.isTR||ap2.isBR){
                        x = tx;
                        al = tw/2;
                    }else if(ap2.isTL||ap2.isBL){
                        x = tx + tw - tpw; 
                        al = tpw-tw/2;
                    }
                    y = ap2.isT ? ty - tph - margin : ty + th + margin;
                }else if(ap2.isL||ap2.isR){
                    arcs = ap2.isL ? this.ars[3] : this.ars[2];
                    if(p=='l'||p=='r'){
                        y = ty + (th-tph)/2;
                        at = tph/2;
                    }else if(ap2.isLB||ap2.isRB){
                        y = ty;
                        at = th/2;
                    }else if(ap2.isLT||ap2.isRT){
                        y = ty + th - tph; 
                        at = tph - th/2;
                    }
                    x = ap2.isL ? tx - tpw - margin : tx + tw + margin;
                }
                tip.setX(x + sl).setY(y + st);
                ar.removeClass(this.ars).addClass(arcs);
                ar.setStyle('left',null).setStyle('top',null);
                (ap2.isT||ap2.isB) ? ar.setStyle('left',al+'px'): ar.setStyle('top',at+'px');
                
        },
        show: function(t){
            if(t&&!Ext.fly(t).hasClass('s-btn-disabled')) {
                if(this.crt == t) return;
                this.crt = t;
                var tip = this.el, text = Ext.fly(t).getAttributeNS('',this.dt),
                    style = Ext.fly(t).getAttributeNS('',this.dt + '-style'), type = Ext.fly(t).getAttributeNS('',this.dt + '-type'),style = (style==''&&type == 'tip') ? 'black': style;
                if(text == '') return;
                if(text.indexOf('call:')==0){
                    var fn = $S.getFunction(text.substring(5,text.length));                
                    if(fn != null) text = fn.call(window, t);                  
                }
                               
                this.el.removeClass(this.ecs);
                this.el.removeClass('s-bubble-tip');
                if(type=='tip') this.el.addClass('s-bubble-tip');
                if(style) this.el.addClass('s-bubble-'+style);
                tip.child('.s-bubble-content-id').update(text);  
                this.restrict(t);
                tip.dom.style.opacity = 1;
            }
        },
        findTarget: function(e){
            var tg = Ext.get(e.target);
            var t = tg.getAttributeNS('',this.dt);
            if(t == ''){
                tg = tg.parent('['+this.dt+']');
                if(tg) t = tg.getAttributeNS('',this.dt);
            }
            return t == '' ? null : tg;
        },
        clear: function(){
            var sf = this;
            if(sf.hid) {
                clearTimeout(sf.hid);
                sf.hid = null;
            }
            if(sf.sid) {
                clearTimeout(sf.sid);
                sf.sid = null;
            }
        },
        onMouseDown: function(e){
            var sf = this;
            var tg = sf.findTarget(e);
            if(tg != null && tg.contains(e.target)){
                sf.clear();
                sf.hide();
                return;
            }
        },
        onMouseOver: function(e){
            e.stopEvent();
            var sf = this;
            if(sf.el.contains(e.target)) {
                if(sf.hid) {
                    clearTimeout(sf.hid);
                    sf.hid = null;
                }
                return;
            }
            var tg = sf.findTarget(e);
            if(tg == null){
                if(sf.hid == null) {
                    sf.clear();
                    sf.hid = sf.hide.defer(100,sf);
                }
            }else {
                sf.clear();
                sf.sid = sf.show.defer(200,sf,[tg]);
            }
        }
    }
    $S.DocMg.register(tip);
    return tip
}();



$S.Cover = function(){
    var m = {
//      bof:null,
        sw:null,
        sh:null,
        container: {},
        cover : function(el,opacity){
//          $S.Cover.bof = Ext.getBody().getStyle('overflow');
            var dd = document.documentElement,db = document.body
            var sw = Ext.isStrict ? dd.scrollWidth : db.scrollWidth;
            var sh = Ext.isStrict ? dd.scrollHeight : db.scrollHeight;
            var screenWidth = Math.max(sw,$S.getViewportWidth());
            var screenHeight = Math.max(sh,$S.getViewportHeight());
            var opt = (opacity) ? 'filter: alpha(opacity='+opacity+');opacity:'+(opacity/100)+';mozopacity:'+(opacity/100) : '';
            var p = '<DIV class="s_cover" style="left:0px;top:0px;width:'+(screenWidth)+'px;height:'+(screenHeight)+'px;'+ opt+'" unselectable="on"></DIV>';
            var cover = Ext.get(Ext.DomHelper.insertFirst(Ext.getBody(),p));
            cover.setStyle('z-index', Ext.fly(el).getStyle('z-index') - 1);
//          Ext.getBody().setStyle('overflow','hidden');
            $S.Cover.container[el.id] = cover;
        },
        uncover : function(el){
            var cover = $S.Cover.container[el.id];
            if(cover) {
                Ext.fly(cover).remove();
                $S.Cover.container[el.id] = null;
                delete $S.Cover.container[el.id];
            }
            var reset = true;
            for(key in $S.Cover.container){
                if($S.Cover.container[key]) {
                    reset = false;  
                    break;
                }
            }
//            if(reset&&$S.Cover.bof)Ext.getBody().setStyle('overflow',$S.Cover.bof);
        },
        resizeCover : function(){
            for(key in $S.Cover.container){
                var cover = $S.Cover.container[key];
                Ext.fly(cover).setStyle('display','none');
            }
            setTimeout(function(){
                var scrollWidth = Ext.isStrict ? document.documentElement.scrollWidth : document.body.scrollWidth;
                var scrollHeight = Ext.isStrict ? document.documentElement.scrollHeight : document.body.scrollHeight;
                var screenWidth = Math.max(scrollWidth,$S.getViewportWidth()) -1;
                var screenHeight = Math.max(scrollHeight,$S.getViewportHeight()) -1;
                for(key in $S.Cover.container){
                    var cover = $S.Cover.container[key];
                    Ext.fly(cover).setWidth(screenWidth);
                    Ext.fly(cover).setHeight(screenHeight);
                    Ext.fly(cover).setStyle('display','');
                }       
            },1)
        }
    }
    return m;
}();
$S.Masker = function(){
    var m = {
        container: {},
        mask : function(el,msg){
            if($S.Masker.container[el.id]){
               return;
            }
            msg = msg||'正在操作...';
            var zindex = 'z-index';
            var el = Ext.get(el);
            var w = el.getWidth();
            var h = el.getHeight();//display:none;
            var p = '<div class="s_mask"  style="left:0px;top:0px;width:'+w+'px;height:'+h+'px;position: absolute;"><div unselectable="on"></div><span style="top:'+(h/2-11)+'px">'+msg+'</span></div>';
            var masker = Ext.get(Ext.DomHelper.insertFirst(el.parent(),p));
            var zi = el.getStyle(zindex) == 'auto' ? 0 : el.getStyle(zindex);
            masker.setStyle(zindex, zi + 1);
            masker.setXY(el.getXY());
            var sp = masker.child('span');
//            var size = $S.TextMetrics.measure(sp,msg);
//            sp.setLeft((w-size.width)/2);
            sp.setLeft((w-sp.getWidth())/2)
            $S.Masker.container[el.id] = masker;
        },
        unmask : function(el){
            var masker = $S.Masker.container[el.id];
            if(masker) {
                Ext.fly(masker).remove();
                $S.Masker.container[el.id] = null;
                delete $S.Masker.container[el.id];
            }
        }
    }
    return m;
}();

$S.evalList = [];
$S.evaling = false;
$S.doEvalScript = function(){
    $S.evaling = true;
    var list = $S.evalList;
    var o = list.shift();
    if(!o) {
        window['__host'] = null;
        $S.evaling = false;
        if($S.loadEvent){
            $S.loadEvent.fire();
            $S.loadEvent = null;
        }
        return;
    }
    var sf = o.sf, html=o.html, loadScripts=o.loadScripts, callback=o.callback, host=o.host,id=o.id;
    var dom = sf.dom;
    
    if(host) window['__host'] = host;
    var links = [];
    var scripts = [];
    var hd = document.getElementsByTagName("head")[0];
    for(var i=0;i<hd.childNodes.length;i++){
        var he = hd.childNodes[i];
        if(he.tagName == 'LINK') {
            links.push(he.href);
        }else if(he.tagName == 'SCRIPT'){
            scripts.push(he.src);
        }
    }
    var jsre = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
    var jsSrcRe = /\ssrc=([\'\"])(.*?)\1/i;
    
    var cssre = /(?:<link([^>]*)?>)((\n|\r|.)*?)/ig;
    var cssHreRe = /\shref=([\'\"])(.*?)\1/i;
    
    var cssm;
    while(cssm = cssre.exec(html)){
        var attrs = cssm[1];
        var srcMatch = attrs ? attrs.match(cssHreRe) : false;
        if(srcMatch && srcMatch[2]){
            var included = false;
            for(var i=0;i<links.length;i++){
                var link = links[i];
                if(link.indexOf(srcMatch[2]) != -1){
                    included = true;
                    break;
                }
            }
            if(!included) {
                var s = document.createElement("link");
                s.type = 'text/css';
                s.rel = 'stylesheet';
                s.href = srcMatch[2];
                hd.appendChild(s);
            }
        }
    }
    var match;
    var jslink = [];
    var jsscript = [];
    while(match = jsre.exec(html)){
        var attrs = match[1];
        var srcMatch = attrs ? attrs.match(jsSrcRe) : false;
        if(srcMatch && srcMatch[2]){
            var included = false;
            for(var i=0;i<scripts.length;i++){
                var script = scripts[i];
                if(script.indexOf(srcMatch[2]) != -1){
                    included = true;
                    break;
                }
            }
            if(!included) {
                jslink[jslink.length] = {
                    src:srcMatch[2],
                    type:'text/javascript'
                }
            } 
        }else if(match[2] && match[2].length > 0){
            jsscript[jsscript.length] = match[2];
        }
    }
    var loaded = 0;
    
    var finishLoad = function(){
        for(j=0,k=jsscript.length;j<k;j++){
            var jst = jsscript[j];
            if(window.execScript) {
                window.execScript(jst);
            } else {
                window.eval(jst);
            }
        }
        var el = document.getElementById(id);
        if(el){Ext.removeNode(el);} 
        Ext.fly(dom).setStyle('display', 'block');
        if(typeof callback == "function"){
            callback();
        }
        $S.doEvalScript();
    }
    
    var continueLoad = function(){
        var js = jslink[loaded];
        var s = document.createElement("script");
        s.src = js.src;
        s.type = js.type;
        s[Ext.isIE ? "onreadystatechange" : "onload"] = onReadOnLoad;
        s["onerror"] = onErrorLoad;
        hd.appendChild(s);        
    }    
    
    var onReadOnLoad = function(){        
        var isready = Ext.isIE ? (!this.readyState || this.readyState == "loaded" || this.readyState == "complete") : true;
        if(isready) {
            loaded ++;
            if(loaded==jslink.length) {
                finishLoad();
            }else{
                continueLoad();
            }
        }
    }
    
    var onErrorLoad = function(evt){
        loaded++;
        alert('无法加载脚本:' + evt.target.src);
        if(loaded==jslink.length) {
            finishLoad();
        }else {
            continueLoad();
        }
    }
    
    if(jslink.length > 0){
        continueLoad();
    } else if(jslink.length ==0) {
        for(j=0,k=jsscript.length;j<k;j++){
            var jst = jsscript[j];
            if(window.execScript) {
               window.execScript(jst);
            } else {
               window.eval(jst);
            }
        }
        var el = document.getElementById(id);
        if(el){Ext.removeNode(el);} 
        Ext.fly(dom).setStyle('display', 'block');
        if(typeof callback == "function"){
                callback();
        }
        $S.doEvalScript();
    } 
}
Ext.Element.prototype.update = function(html, loadScripts, callback,host){
    if(typeof html == "undefined"){
        html = "";
    }
    if(loadScripts !== true){
        this.dom.innerHTML = html;
        if(typeof callback == "function"){
            callback();
        }
        return this;
    }
    
    var id = Ext.id();
    var sf = this;
    var dom = this.dom;
    html += '<span id="' + id + '"></span>';
    Ext.lib.Event.onAvailable(id, function(){
        $S.evalList.push({
            html:html,
            loadScripts:loadScripts,
            callback:callback,
            host:host,
            id:id,
            sf:sf
        });
        if(!$S.evaling)
        $S.doEvalScript() 
    });
    
    Ext.fly(dom).setStyle('display', 'none');
    dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "").replace(/(?:<link.*?>)((\n|\r|.)*?)/ig, "");
    return this;
}

Ext.EventObjectImpl.prototype['isSpecialKey'] = function(){
    var k = this.keyCode;
    return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 || //9-TAB  13-ENTER  40-DOWN  27-ESC
    (k == 16) || (k == 17) ||  //16-SHIFT-L   17-CONTROL-L
    (k >= 18 && k <= 20) ||  //18-ALT-L   19-PAUSE  20-CAP-LOCK
    (k >= 33 && k <= 35) ||
    (k >= 36 && k <= 39) ||
    (k >= 44 && k <= 45);
}
Ext.removeNode = Ext.isIE && !Ext.isIE8 ? function(){
    var d;
    return function(n){
        if(n && n.tagName != 'BODY'){
            (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
            if(!d){
                d = document.createElement('div');
                d.id = '_removenode';
                d.style.cssText = 'position:absolute;display:none;left:-1000px;top:-1000px';
            }
//            d = d || document.createElement('<div id="_removenode" style="position:absolute;display:none;left:-1000px;top:-1000px">');
            if(!d.parentNode)document.body.appendChild(d);
            d.appendChild(n);
            d.innerHTML = '';
            delete Ext.elCache[n.id];
        }
    }
}() : function(n){
    if(n && n.parentNode && n.tagName != 'BODY'){
        (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n, true) : Ext.EventManager.removeAll(n);
        n.parentNode.removeChild(n);
        delete Ext.elCache[n.id];
    }
}
//$S.parseDate = function(str){
//    if(typeof str == 'string'){        
//        var results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) *$/);      
//        if(results && results.length>3)      
//            return new Date(parseInt(results[1]),parseInt(results[2],10) -1,parseInt(results[3],10));       
//        results = str.match(/^ *(\d{4})-(\d{1,2})-(\d{1,2}) +(\d{1,2}):(\d{1,2}):(\d{1,2}) *$/);  
//        if(results && results.length>6)      
//        return new Date(parseInt(results[1]),parseInt(results[2],10) -1,parseInt(results[3],10),parseInt(results[4],10),parseInt(results[5],10),parseInt(results[6],10));       
//    }      
//    return null;      
//}
$S.getFunction = function(fn){
    if(!fn) return null;
    if(fn instanceof Function) return fn;
    var rder;
    try{
        if(fn.indexOf('.') != -1){
            var p = fn.substr(0,fn.lastIndexOf('.')),f = fn.substr(fn.lastIndexOf('.')+1);
            if(window.eval) {
               rder = window.eval(p)[f];
            } else if(window.execScript){
               rder = window.execScript(fn)[f];
            }
        }else{
            rder = window[fn];
        }
    } catch(e){
        alert('无法找到' + fn)
    }    
//    if(fn.indexOf('.') != -1){
//        rder = $S[fn.substr(6,fn.length)]
//    }else{
//        rder = window[fn];
//    }
    return rder;
}


$S.formatMoney = function(mnum) {
    mnum = Math.abs(mnum);
    var unitArray = [["元", "万", "亿"], ["仟", "", "拾", "佰"],["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"]];
    totalarray = new Array();

    totalarray = mnum.toString().split(".");
    if (totalarray.length == 1) {
        totalarray[1] = "00"
    } else if (totalarray[1].length == 1) {
        totalarray[1] += '0';
    }
    integerpart = new Array();
    decimalpart = new Array();
    var strout = "";
    for (var i = 0; i < totalarray[0].length; i++) {
        integerpart[i] = totalarray[0].charAt(i);
    }
    for (var i = 0; i < totalarray[1].length; i++) {
        decimalpart[i] = totalarray[1].charAt(i);
    }
    for (var i = 0; i < integerpart.length; i++) {
        var strTemp = (integerpart.length - i - 1) % 4 == 0
                ? unitArray[0][parseInt((integerpart.length - i) / 4)]
                : (integerpart[i] == 0)
                        ? ""
                        : unitArray[1][((integerpart.length - i) % 4)]
        strout = strout + unitArray[2][integerpart[i]] + strTemp;
    }
    strout = strout.replace(new RegExp(/零+/g), "零");
    strout = strout.replace("零万", "万");
    strout = strout.replace("零亿", "亿");
    strout = strout.replace("零元", "元");
    strout = strout.replace("亿万", "亿");
    var strdec = ""
    if (decimalpart[0] == 0 && decimalpart[1] == 0) {
        strdec = "整";
    } else {
        if (decimalpart[0] == 0) {
            strdec = "零"
        } else {
            strdec = unitArray[2][decimalpart[0]] + '角';
        }
        if (decimalpart[1] != 0) {
            strdec += unitArray[2][decimalpart[1]] + '分';
        }
    }
    strout += strdec;
    if (mnum < 0)
        strout = "负" + strout;
    return strout;
}
/************** Renderer **************/
$S.escapeHtml = function(str){
    if(Ext.isEmpty(str) || !Ext.isString(str)) return str;
    return String(str).replace(/&/gm,'&amp;').replace(/</gm,'&lt;').replace(/>/gm,'&gt;');
}
$S.unescapeHtml = function(str){
    if(Ext.isEmpty(str) || !Ext.isString(str)) return str;
    return String(str).replace(/&amp;/gm,'&').replace(/&lt;/gm,'<').replace(/&gt;/gm,'>');
}
$S.RowNumberRenderer = function(value,record,name){
    if(record && record.ds){
        var ds = record.ds;
        return (ds.currentPage-1)*ds.psize + ds.indexOf(record) + 1;
    }
}
$S.formatDate = function(date){
    return (!date) ? '' : date.format($S.defaultDateFormat);
}
$S.formatDateTime = function(date){
    return (!date) ? '' : date.format('isoDateTime');
}
$S.moneyRenderer = function(v){
    return $S.formatNumber(v,2); 
}
$S.formatNumber = function(value,decimalprecision){
    if(value!==0&&(!value||isNaN(value)))return '';
    if(decimalprecision||decimalprecision===0) value=parseFloat(value).toFixed(decimalprecision);
    var ps = String(value).split('.');
    var sub = (ps.length==2)?'.'+ps[1]:'';
    var whole = ps[0];
    var r = /(\d+)(\d{3})/;
    while (r.test(whole)) {
        whole = whole.replace(r, '$1' + ',' + '$2');
    }
    v = whole + sub;
    return v;   
}
$S.removeNumberFormat = function(rv){
    rv = String(rv||'');
    while (rv.indexOf(',')!=-1) {
        rv = rv.replace(',', '');
    }
    return isNaN(rv) ? parseFloat(rv) : rv;
}

$S.EventManager = Ext.extend(Ext.util.Observable,{
    constructor: function() {
        $S.EventManager.superclass.constructor.call(this);
        this.initEvents();
    },
    initEvents : function(){
        this.addEvents(
            'ajaxerror',
            'ajaxsuccess',
            'ajaxfailed',
            'ajaxstart',
            'ajaxcomplete',
            'valid',
            'timeout'
        );      
    }
});
$S.EM = new $S.EventManager();
$S.regEvent = function(name, hanlder){
    $S.EM.on(name, hanlder);
}
Ext.fly(document.documentElement).on('keydown',function(e,t){
    var tagName = t.tagName.toUpperCase();
    e.keyCode == 8 && ((tagName != 'INPUT' && tagName != 'TEXTAREA') || t.readOnly) && e.stopEvent();
})
//$S.setCookie('_isbox',Ext.isBorderBox);
/**
 * @class Spark.DataSet
 * @extends Ext.util.Observable
 * <p>DataSet是一个数据源，也是一个数据集合，它封装了所有数据的操作，校验，提交等操作.
 * @author niujiaqing
 * @constructor
 * @param {Object} config 配置对象. 
 * 
 */
$S.AUTO_ID = 1000;
$S.DataSet = Ext.extend(Ext.util.Observable,{
	constructor: function(cfg) {
		$S.DataSet.superclass.constructor.call(this);
		cfg = cfg || {};
		if(cfg.listeners){
            this.on(cfg.listeners);
        }
        
        this.id = cfg.id || Ext.id();
        $S.CmpManager.put(this.id,this);
        
        this.fields = {};
        //submit parameter
    	this.sp = {};
    	//query  parameter
    	this.qp = {};
    	
    	this.selected = [];
        
        this.sortModel = cfg.sortModel||'local';
        this.defaultSortField = cfg.defaultSortField;
        this.defaultSortType = cfg.defaultSortType;
        if(cfg.defaultSortField) this.setQueryParameter('ORDER_FIELD', cfg.defaultSortField);
        if(cfg.defaultSortType) this.setQueryParameter('ORDER_TYPE', cfg.defaultSortType);
        
    	//page size
    	this.psize = cfg.psize || 10;
    	
    	//submit url
    	this.surl = cfg.surl || '';
    	//query url
    	this.qurl = cfg.qurl || '';
    	
    	//selectable
    	this.sb = cfg.sb||false;
    	//selection model
    	this.sm = cfg.sm||'multiple'; 
    	//selectfunction
    	this.sft = cfg.sft;
    	this.checkDirty = cfg.checkDirty;
        
    	//bind target
    	if(cfg.bt)this.bt = cfg.bt;
    	
    	//bind name
    	if(cfg.bn)this.bn = cfg.bn;
        
        this.loading = false;
    	//loadingTip
        this.loadingTip = cfg.loadingTip;
        this.successTip = cfg.successTip;
        this.lock = cfg.lock;
        
    	this.clear();
        
        if(this.bt&&this.bn) this.bind($(this.bt),this.bn);
    	this.qds = Ext.isEmpty(cfg.qds) ? null : $(cfg.qds);
    	if(this.qds != null && this.qds.current() == null) this.qds.create();
    	
    	this.initEvents();
    	if(cfg.fields) this.initFields(cfg.fields)
    	if(cfg.datas && cfg.datas.length != 0) {
           this.loadData(cfg.datas);
           this.locate(this.currentIndex,true);
    	}else if(cfg.notFoundMessage){
            var cb = $S.getFunction(cfg.notFoundCallback)
            $S.showInfoMessage('提示', cfg.notFoundMessage, cb);
            return;
        }
    	//auto query
    	if(cfg.aq === true) {
            var sf = this;
            $S.onReady(function(){
               sf.query(); 
            });
    	}
    	//auto create
    	if(cfg.ac == true) {
            if(this.datas.length == 0)
            this.create();
    	}
    },
    destroy : function(){
    	if(this.bt && this.bn){
            var bd = $S.CmpManager.get(this.bt)
            if(bd) bd.unBind();
    	}
    	$S.CmpManager.remove(this.id);
    },
    reConfig : function(config){
    	this.clear();
    	Ext.apply(this, config);
    },
    /**
     * 取消绑定.
     */
    unBind : function(){
    	var name = this.bn;
        var ds = this.fields[name].pro['dataset'];
        if(ds)
        ds.processBindDataSetListener(this,'un');
        delete this.fields[name];
    },
    processBindDataSetListener : function(ds,ou){
        var bdp = this.onDataSetModify;
        this[ou]('add', bdp, this);
        this[ou]('remove', bdp, this);
        this[ou]('update', bdp, this);
        this[ou]('clear', bdp, this);
        this[ou]('indexchange', bdp, this);
        this[ou]('load', this.onDataSetLoad, this);
        this[ou]('reject', bdp, this);
        ds[ou]('indexchange',this.onDataSetIndexChange, this);
        ds[ou]('load',this.onBindDataSetLoad, this);
        ds[ou]('remove',this.onBindDataSetLoad, this);
        ds[ou]('clear',this.removeAll, this);
    },
    /**
     * 将当前DS绑定到某个DS的Field上.
     * @param {Spark.DataSet} dataSet 绑定的DataSet.
     * @param {String} name Field的name. 
     * 
     */
    bind : function(ds, name){
        if(ds.fields[name]) {
            return;
        }
        this.processBindDataSetListener(ds,'un');
        this.processBindDataSetListener(ds,'on');
        var field = new $S.Record.Field({
            name:name,
            type:'dataset',
            dataset:this
        });
        ds.fields[name] = field;
    },
    /**
     * 是否存在修改的record
     * @return {Boolean}
     */
    isDirty : function(){
        var dirty = false;
        if(this.checkDirty)
        for(var i = 0,len = this.datas.length; i < len; i++){
            if(dirty)break;
            var r = this.datas[i];
            if(r.isDirty){
                dirty = true
            }else {
                for(var k in this.fields){
                    var field = this.fields[k];
                    if(field.type == 'dataset'){
                        var ds = field.pro['dataset'];
                        if(r && r.data[field.name]){
                            ds.reConfig(r.data[field.name]);
                            dirty = ds.isDirty();
                            if(dirty)break;
                        }
                    }
                }
            }
        }
        
        return dirty;
    },
    onBindDataSetLoad : function(ds,options){
        if(ds.getAll().length == 0) this.removeAll();
    },
    onDataSetIndexChange : function(ds, record){
    	if(!record.get(this.bn) && record.isNew != true){
    		this.qp = {};
            Ext.apply(this.qp,record.data);
            this.query(1,{record:record});
    	} 	
    },
    onDataSetModify : function(){
    	var bt = $S.CmpManager.get(this.bt);
    	if(bt){
            this.refreshBindDataSet(bt.current(),this.getConfig())
    	}
    },
    onDataSetLoad : function(ds,options){
    	var record;
    	if(options){
    		record = options.opts.record;
    	}else{
    		var bt = $S.CmpManager.get(this.bt);
    		if(bt) record = bt.current();    		
    	}
    	if(record)
    	this.refreshBindDataSet(record,ds.getConfig())
    },
   	refreshBindDataSet: function(record,config){
    	if(!record)return;
    	record.set(this.bn,config,true)
    },
    clear : function(){
    	this.datas = [];
        this.fields = {};
    	this.selected = [];
    	this.gotoPage = 1;
    	this.currentPage = 1;
    	this.currentIndex = 1;
    	this.totalCount = 0;
    	this.totalPage = 0;
    	this.isValid = true;
    },
    getConfig : function(){
    	var c = {};
    	c.xtype = 'dataset';
        c.fields = this.fields;
    	c.datas = this.datas;
    	c.selected = this.selected;
    	c.isValid = this.isValid;
    	c.gotoPage = this.gotoPage;
    	c.currentPage = this.currentPage;
    	c.currentIndex = this.currentIndex;
    	c.totalCount = this.totalCount;
    	c.totalPage = this.totalPage;
    	return c;
    },
    initEvents : function(){
    	this.addEvents( 
    	    /**
             * @event add
             * 数据增加事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 增加的record.
             * @param {Number} index 指针.
             */
            'add',
            /**
             * @event ajaxfailed
             * ajax调用失败.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */
            'ajaxfailed',
    	    /**
             * @event beforecreate
             * 数据创建前事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @return {boolean} result 返回结果.如果为false则中断create,默认返回true
             */
    		'beforecreate',
	        /**
             * @event beforeremove
             * 数据删除前.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @return {boolean} result 返回结果.如果为false则中断remove,默认返回true
             */
            'beforeremove',
	        /**
             * @event beforeload
             * 准备加载数据事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Array} datas record集合.
             */ 
	        'beforeload',
	        /**
             * @event beforesubmit
             * 数据提交前事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @return {boolean} result 返回结果.如果为false则中断submit,默认返回true
             */
            'beforesubmit',
            /**
             * @event clear
             * 清除数据事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */
            'clear',
	        /**
             * @event fieldchange
             * field配置改变事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 当前的record.
             * @param {Spark.Record.Field} field Field配置对象.
             * @param {String} type 类型.
             * @param {Object} value 值.
             */
            'fieldchange',
            /**
             * @event indexchange
             * DataSet当前指针改变事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 当前record.
             */ 
            'indexchange',
            /**
             * @event load
             * 加载数据事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */ 
	        'load',
	        /**
             * @event loadfailed
             * 加载数据失败.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */ 
            'loadfailed',
            /**
             * @event query
             * 查询事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */ 
            'query',
	        /**
             * @event refresh
             * 刷新事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */ 
	        'refresh',	
	        /**
             * @event remove
             * 数据删除事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 删除的record.
             * @param {Number} index 指针.
             */
            'remove',
            /**
             * @event reject
             * 数据重置事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 取消选择的record.
             * @param {String} name 重置的field.
             * @param {Object} value 重置的值.
             */
            'reject',
            /**
             * @event select
             * 选择数据事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 选择的record.
             */ 
            'select',
            /**
             * @event submit
             * 数据提交事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */
            'submit',
            /**
             * @event submitsuccess
             * 数据提交成功事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */
            'submitsuccess',
            /**
             * @event submitfailed
             * 数据提交失败事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             */
            'submitfailed',
	        /**
             * @event update
             * 数据更新事件.
             * "update", this, record, name, value
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 更新的record.
             * @param {String} name 更新的field.
             * @param {Object} value 更新的值.
             */
            'update',
	        /**
             * @event select
             * 取消选择数据事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 取消选择的record.
             */
	        'unselect',
	        /**
             * @event valid
             * DataSet校验事件.
             * @param {Spark.DataSet} dataSet 当前DataSet.
             * @param {Spark.Record} record 校验的record.
             * @param {String} name 校验的field.
             * @param {Boolean} valid 校验结果. true 校验成功  false 校验失败
             */ 
            'valid'
		);    	
    },
    initFields : function(fields){
    	for(var i = 0, len = fields.length; i < len; i++){
    		var field = new $S.Record.Field(fields[i]);
	        this.fields[field.name] = field;
        }
    },
    /**
     * 获取dataSet级别的Field
     * @param {String} name Field的name. 
     * @return {Spark.Record.Field} field配置对象
     */
    getField : function(name){
    	return this.fields[name];
    },
    clearFilter : function(){
        if(this.backup){
            this.datas = this.backup;
            delete this.backup;
        }
    },
    filter : function(callback, scope){
        this.clearFilter();
        var d = this.backup||this.datas,nd = [];
        this.backup = d;
        Ext.each(d,function(o){
            if(callback.call(scope||this,o)!==false){
                nd.push(o); 
            }
        },this)
        this.datas = nd;
    },
    loadData : function(datas, num, options){  
        this.clearFilter();
        this.datas = [];
        this.selected = [];
        if(num) {
        	this.totalCount = num;
        }else{
        	this.totalCount = datas.length;
        }
    	this.totalPage = (this.psize == -1) ? 1 : Math.ceil(this.totalCount/this.psize)
    	for(var i = 0, len = datas.length; i < len; i++){
    		var data = datas[i].data||datas[i];
    		for(var key in this.fields){
    			var field = this.fields[key];
    			if(field){
                    var val = this.processData(data,key,field);
                    if(val) data[key] = val; 
    			}
    		}
    		var record = new $S.Record(data,datas[i].field);
            record.ds = this;
	        this.datas.push(record);
        }
//        if(this.sortInfo) this.sort();
        
        this.fireEvent("beforeload",this,this.datas);
        var needFire = true;
        if(this.bt && options){
           var cr = $S.CmpManager.get(this.bt).current();
           if(options.opts.record && cr!=options.opts.record){
               this.refreshBindDataSet(options.opts.record,this.getConfig());
               needFire = false;
           }
        }
        if(needFire)
        this.fireEvent("load", this, options);
    },
    sort : function(f, direction){
    	if(this.getAll().length==0)return;
        if(this.sortModel == 'remote'){
            if(direction=='') {
                delete this.qp['ORDER_FIELD'];
                delete this.qp['ORDER_TYPE'];
            }else{
                this.setQueryParameter('ORDER_FIELD', f);
                this.setQueryParameter('ORDER_TYPE', direction);
            }
            this.query();
        }else{
            this.datas.sort(function(a, b){
                var rs = a.get(f) > b.get(f)
                return (direction == 'desc' ? (rs ? -1 : 1) : (rs ? 1 : -1));
            })
            this.fireEvent('refresh',this);
        }
    },
    /**
     * 创建一条记录
     * @param {Object} data 数据对象
     * @return {Spark.Record} record 返回创建的record对象
     */
    create : function(data){
    	if(this.fireEvent("beforecreate", this)){
        	data = data||{}
        	var dd = {};
        	for(var k in this.fields){
        		var field = this.fields[k];
        		var dv = field.getPro('defaultValue');
                if(dv && !data[field.name]){
                    dd[field.name] = dv;
                }
        	}
            for(var k in this.fields){
                var field = this.fields[k];
                 var dv = this.processValueListField(dd,data[field.name],field);
                 if(dv) dd[field.name] = dv;
            }
        	var data = Ext.apply(data||{},dd);
        	var record = new $S.Record(data);
            this.add(record); 
            return record;
    	}
    },
    /**
     * 获取所有新创建的数据. 
     * @return {Array} 所有新创建的records
     */
    getNewRecrods: function(){
        var records = this.getAll();
        var news = [];
       	for(var k = 0,l=records.length;k<l;k++){
			var record = records[k];
			if(record.isNew == true){
				news.push(record);
			}
		}
		return news;
    },
    /**
     * 新增数据. 
     * @param {Spark.Record} record 需要新增的Record对象. 
     */
    add : function(record){
    	record.isNew = true;
        record.ds = this;
        var index = this.datas.length;
        this.datas.add(record);
        var index = (this.currentPage-1)*this.psize + this.datas.length;
        this.currentIndex = index;
        this.fireEvent("add", this, record, index);
        this.locate(index, true);
    },
    /**
     * 获取当前指针的Record. 
     * @return {Spark.Record} 当前指针所处的Record
     */
    current : function(){
    	if(this.datas.length ==0) return null;
    	return this.datas[this.currentIndex - (this.currentPage-1)*this.psize -1];
    },
    /**
     * 插入数据. 
     * @param {Number} index  指定位置. 
     * @param {Array} records 需要新增的Record对象集合.
     */
    insert : function(index, records){
        records = [].concat(records);
        var splice = this.datas.splice(index,this.datas.length);
        for(var i = 0, len = records.length; i < len; i++){
            records[i].ds = this;
            this.datas.add(records[i]);
        }
        this.datas = this.datas.concat(splice);
        this.fireEvent("add", this, records, index);
    },
    /**
     * 移除数据.  
     * @param {Spark.Record} record 需要移除的Record.
     */
    remove : function(record){  
    	if(!record){
    		record = this.current();
    	}
    	if(!record)return;
    	//根据beforeremove的返回值决定是否删除数据
    	if(this.fireEvent("beforeremove", this)){
        	var rs = [].concat(record);
        	var rrs = [];
        	for(var i=0;i<rs.length;i++){
        		var r = rs[i]
        		if(r.isNew){
                    this.removeLocal(r);
        		}else{    		
                    rrs[rrs.length] = r;
        		}
        	}
        	this.removeRemote(rrs);
    	}
    },
    removeRemote: function(rs){    	
    	if(this.surl == '') return;
    	var p = [];
    	for(var k=0;k<rs.length;k++){
    		var r = rs[k]
    		for(var key in this.fields){
                var f = this.fields[key];
                if(f && f.type == 'dataset') delete r.data[key];
            }
        	var d = Ext.apply({}, r.data);
    		d['_id'] = r.id;
    		d['_status'] = 'delete';
            p[k] = Ext.apply(d,this.sp)
    	}
    	if(p.length > 0) {
    		var opts;
    		if(this.bt){
                var bd = $S.CmpManager.get(this.bt);
                opts = {record:bd.current(),dataSet:this};
    		}
	    	$S.request({url:this.surl, para:p, success:this.onRemoveSuccess, error:this.onSubmitError, scope:this, failure:this.onAjaxFailed,opts:opts});
    	}
    
    },
    onRemoveSuccess: function(res,options){
    	if(res.result){
    		var datas = [].concat(res.result);
    		if(this.bt){
                var bd = $S.CmpManager.get(this.bt);
                if(bd.current()==options.opts.record){
                    for(var i=0;i<datas.length;i++){
                        var data = datas[i];
                        this.removeLocal(this.findById(data['_id']),true); 
                    }
                }else{
                    var config = options.opts.record.get(this.bn);
                    var ds = new $S.DataSet({});
                    ds.reConfig(config);
                    for(var i=0;i<datas.length;i++){
                        var data = datas[i];
                        ds.removeLocal(ds.findById(data['_id']),true);
                    }
                    this.refreshBindDataSet(options.opts.record,ds.getConfig())
                    delete ds;
                }
            }else{
                for(var i=0;i<datas.length;i++){
                    var data = datas[i];
                    this.removeLocal(this.findById(data['_id']),true); 
                }
            }
    	}
    },
    removeLocal: function(record,count){
//    	$S.removeInvalidReocrd(this.id, record)
    	var index = this.datas.indexOf(record);    	
    	if(index == -1)return;
        this.datas.remove(record);
        if(count) this.totalCount --;
        this.selected.remove(record);
        //忘记这块代码的含义了.暂时去掉！
//        if(this.datas.length == 0){
//        	this.removeAll();
//        	return;
//        }
        var lindex = this.currentIndex - (this.currentPage-1)*this.psize;
        if(lindex<0)return;
        if(lindex<=this.datas.length){
        	this.locate(this.currentIndex,true);
        }else{
        	this.pre();
        }
        this.fireEvent("remove", this, record, index);
    },
    /**
     * 获取当前数据集下的所有数据.  
     * @return {Array} records 当前数据集的所有Record.
     */
    getAll : function(){
    	return this.datas;    	
    },
    /**
     * 查找数据.  
     * @param {String} property 查找的属性.
     * @param {Object} value 查找的属性的值.
     * @return {Spark.Record} 符合查找条件的第一个record
     */
    find : function(property, value){
    	var r = null;
    	this.each(function(record){
    		var v = record.get(property);
    		if(v ==value){
    			r = record;
    			return false;    			
    		}
    	}, this)
    	return r;
    },
    /**
     * 根据id查找数据.  
     * @param {String} id id.
     * @return {Spark.Record} 查找的record
     */
    findById : function(id){
    	var find = null,datas = this.backup||this.datas;
    	for(var i = 0,len = datas.length; i < len; i++){
            if(datas[i].id == id){
            	find = datas[i]
                break;
            }
        }
        return find;
    },
    /**
     * 删除所有数据.
     */
    removeAll : function(){
    	this.currentIndex = -1;
        this.totalCount = 0;
        this.datas = [];
        this.selected = [];
        this.fireEvent("clear", this);
    },
    /**
     * 返回指定record的位置
     * @return {Number} record所在的位置
     */
    indexOf : function(record){
        return this.datas.indexOf(record);
    },
    /**
     * 获取指定位置的record
     * @param {Number} 位置
     */
    getAt : function(index){
        return this.datas[index];
    },
    each : function(fn, scope){
        var items = [].concat(this.backup||this.datas); // each safe for removal
        for(var i = 0, len = items.length; i < len; i++){
            if(fn.call(scope || items[i], items[i], i, len) === false){
                break;
            }
        }
    },
    processCurrentRecord : function(){
    	var r = this.current();
    	for(var k in this.fields){
    		var field = this.fields[k];
    		if(field.type == 'dataset'){
    			var ds = field.pro['dataset'];
    			if(r && r.data[field.name]){
    				ds.reConfig(r.data[field.name]);
    			}else{
    				ds.clear();
    			}
    			ds.fireEvent('refresh',ds);
    			ds.processCurrentRecord();
    		}
    	}
    	if(r) this.fireEvent("indexchange", this, r);
    },
    /**
     * 获取所有选择的数据.
     * @return {Array} 所有选择数据.
     */
    getSelected : function(){
    	return this.selected;
    },
    /**
     * 选择所有数据.
     */
    selectAll : function(){
    	for(var i=0,l=this.datas.length;i<l;i++){
    		this.select(this.datas[i]);
    	}
    },
    /**
     * 取消所有选择.
     */
    unSelectAll : function(){
    	for(var i=0,l=this.datas.length;i<l;i++){
    		this.unSelect(this.datas[i]);
    	}
    },
    /**
     * 选择某个record.
     * @param {Spark.Record} record 需要选择的record.
     */
    select : function(r){
    	if(!this.sb)return;
    	if(typeof(r) == 'string'||typeof(r) == 'number') r = this.findById(r);
    	if(this.selected.indexOf(r) != -1)return;
        if(!this.execSelectFunction(r))return;
    	if(this.sb && this.sm == 'multiple'){
    		if(this.selected.indexOf(r) == -1) {
    			this.selected.push(r);
    			this.fireEvent('select', this, r);
    		}
       	}else{
       		if(this.selected.indexOf(r) == -1) {
	       		var or = this.selected[0];
	       		this.unSelect(or);
	       		this.selected = []
	       		this.selected.push(r);
	       		this.fireEvent('select', this, r);
       		}
       	}
    },
    execSelectFunction:function(r){
        if(this.sft){
            var selfun = $S.getFunction(this.sft);
            if(selfun == null){
                alert("未找到"+this.sft+"方法!")
            }else{
                var b=selfun.call(window,r);
                if(Ext.isDefined(b))return b;
            }
        }
        return true;
    },
    /**
     * 取消选择某个record.
     * @param {Spark.Record} record 需要取消选择的record.
     */
    unSelect : function(r){
        if(!this.sb)return;
        if(typeof(r) == 'string'||typeof(r) == 'number') r = this.findById(r);
    	if(this.sb){
    		if(this.selected.indexOf(r) != -1) {
    			this.selected.remove(r);
    			this.fireEvent('unselect', this, r);
    		}
    	}
    },
    /**
     * 定位到某个指针位置.
     * @param {Number} index 指针位置.
     */
    locate : function(index, force){
    	if(this.currentIndex == index && force !== true) return;
    	if(index <=0 || (index > this.totalCount + this.getNewRecrods().length))return;
    	var lindex = index - (this.currentPage-1)*this.psize;
    	if(this.datas[lindex - 1]){
	    	this.currentIndex = index;
    	}else{
    		if(this.isModified()){
    			$S.showInfoMessage('提示', '有未保存数据!')
    		}else{
				this.currentIndex = index;
				this.currentPage =  Math.ceil(index/this.psize);
				this.query(this.currentPage);
				return;
    		}
    	}
    	this.processCurrentRecord();
    },
    /**
     * 定位到某页.
     * @param {Number} page 页数.
     */
    page : function(page){
    	if(page >0) {
    		this.gotoPage = page;
            var go = (page-1)*this.psize + 1;
            var news = this.getAll().length-this.psize;
            if(this.currentPage < page && news > 0)go+=news;
//	    	var go = (page-1)*this.psize + this.getNewRecrods().length +1;
	    	this.locate(go);
    	}
    },
    /**
     * 定位到所有数据的第一条位置.
     */
    first : function(){
    	this.locate(1);
    },
    /**
     * 向前移动一个指针位置.
     */
    pre : function(){
    	this.locate(this.currentIndex-1);    	
    },
    /**
     * 向后移动一个指针位置.
     */
    next : function(){
    	this.locate(this.currentIndex+1);
    },
    /**
     * 定位到第一页.
     */
    firstPage : function(){
    	this.page(1);
    },
    /**
     * 刷新当前页
     */
    refresh : function(){
        this.query(this.currentPage);
    },
    /**
     * 向前移动一页.
     */
    prePage : function(){
    	this.page(this.currentPage -1);
    },
    /**
     * 向后移动一页.
     */
    nextPage : function(){
    	this.page(this.currentPage +1);
    },
    /**
     * 定位到最后一页.
     */
    lastPage : function(){
    	this.page(this.totalPage);
    },
    /**
     * 对当前数据集进行校验.
     * @return {Boolean} valid 校验结果.
     */
    validate : function(fire){
    	this.isValid = true;
    	var current = this.current();
    	var records = this.getAll();
		var dmap = {};
		var hassub = false;
		var ur = null;
					
    	for(var k in this.fields){
    		var field = this.fields[k];
    		if(field.type == 'dataset'){
    			hassub = true;
    			var d = field.pro['dataset'];
    			dmap[field.name] = d;
    		}
    	}
    	for(var k = 0,l=records.length;k<l;k++){
			var record = records[k];
			//有些项目是虚拟的字段,例如密码修改
//			if(record.isDirty == true || record.isNew == true) {
				if(!record.validate()){
					this.isValid = false;
					ur = record;
//					$S.addInValidReocrd(this.id, record);
				}else{
//					$S.removeInvalidReocrd(this.id, record);
				}
				if(this.isValid == false) {
					if(hassub)break;
				}else {
					for(var key in dmap){
						var ds = dmap[key];
						if(record.data[key]){
    						ds.reConfig(record.data[key]);
    						if(!ds.validate(false)) {
    							this.isValid = false;
    							ur = record;
    						}
    						ds.reConfig(current.data[key]);//循环校验完毕后,重新定位到当前行
						}
					}
					
					if(this.isValid == false) {
						break;
					}				
//				}
			}
		}
		
		if(ur != null){
			var r = this.indexOf(ur);
			if(r!=-1)this.locate(r+1);
		}
		if(fire !== false) {
			$S.EM.fireEvent('valid', $S.EM, this, this.isValid);
    		if(!this.isValid) $S.showInfoMessage('提示', '验证不通过!');  
		}
		//TODO: 希望能报出具体的校验信息,那个字段,什么原因.
		//TODO: 多语言.
		return this.isValid;
    },
    /**
     * 设置查询的Url.
     * @param {String} url 查询的Url.
     */
    setQueryUrl : function(url){
    	this.qurl = url;
    },
    /**
     * 设置查询的参数.
     * @param {String} para 参数名.
     * @param {Object} value 参数值.
     */
    setQueryParameter : function(para, value){
        this.qp[para] = value;
    },
    /**
     * 设置查询的DataSet.
     * @param {Spark.DataSet} ds DataSet.
     */
    setQueryDataSet : function(ds){ 
    	this.qds = ds;
    	if(this.qds.current() == null) this.qds.create();
    },
    /**
     * 设置提交的Url.
     * @param {String} url 提交的Url.
     */
    setSubmitUrl : function(url){
    	this.surl = url;
    },
    /**
     * 设置提交的参数.
     * @param {String} para 参数名.
     * @param {Object} value 参数值.
     */
    setSubmitParameter : function(para, value){
        this.sp[para] = value;
    },
    /**
     * 查询数据.
     * @param {Number} page(可选) 查询的页数.
     */
    query : function(page,opts){
        if(!this.qurl) return;
        if(this.qds) {
            if(this.qds.current() == null) this.qds.create();
            var sf=this,intervalId=setInterval(function(){
                if(!sf.qds.current().isReady)return;
                clearInterval(intervalId);
                if(!sf.qds.validate()) return;
                sf.doQuery(page,opts);
            },10);
        }else{
            this.doQuery(page,opts);
        }
    },
    
    doQuery : function(page,opts){
        var r;
        if(this.qds)r = this.qds.current();
    	if(!page) this.currentIndex = 1;
    	this.currentPage = page || 1;
    	
    	var q = {};
    	if(r != null) Ext.apply(q, r.data);
    	Ext.apply(q, this.qp);
    	for(var k in q){
    	   var v = q[k];
    	   if(Ext.isEmpty(v,false)) delete q[k];
    	}
    	var para = '_ps='+this.psize + '&_pn='+this.currentPage
    	var url = '';
    	if(this.qurl.indexOf('?') == -1){
    		url = this.qurl + '?' + para;
    	}else{
    		url = this.qurl + '&' + para;
    	}
        this.loading = true;
    	this.fireEvent("query", this);
        if(this.qtId) Ext.Ajax.abort(this.qtId);
    	this.qtId = $S.request({url:url, para:q, success:this.onLoadSuccess, error:this.onLoadError, scope:this,failure:this.onAjaxFailed,opts:opts});
    },
    /**
     * 判断当前数据集是否发生改变.
     * @return {Boolean} modified 是否发生改变.
     */
    isModified : function(){
    	var modified = false;
    	var records = this.getAll();
		for(var k = 0,l=records.length;k<l;k++){
			var record = records[k];
			if(record.isDirty == true || record.isNew == true) {
				modified = true;
				break;
			}else{
                for(var key in this.fields){
                    var field = this.fields[key];
                    if(field.type == 'dataset'){                
                        var ds = field.pro['dataset'];
                        ds.reConfig(record.data[field.name]);
                        if(ds.isModified()){
                            modified = true;
                            break;
                        }
                    }
                }
			}
		}
		return modified;
    },
    /**
     * 获取需要提交的数据.(编辑||新增)
     * @return {Object} 返回的对象.
     */
    getAllDatas : function(data){
    	var datas = [];
    	var items = data||this.datas;
//    	if(selected) items = this.getSelected();
    	for(var i=0,l=items.length;i<l;i++){
    		var r = items[i];
    		var isAdd = r.isDirty || r.isNew
			var d = Ext.apply({}, r.data);
			d['_id'] = r.id;
			d['_status'] = r.isNew ? 'insert' : 'update';
			for(var k in r.data){
				var item = d[k]; 
				if(item && item.xtype == 'dataset'){
					var ds = new $S.DataSet({});//$(item.id);
					ds.reConfig(item)
					isAdd = isAdd == false ? ds.isModified() :isAdd;
					d[k] = ds.getAllDatas();
				}
			}
    		if(isAdd||data){
	    		datas.push(d);    			
			}
    	}
    	return datas;
    },
    doSubmit : function(url, items, fields, success){
        //if(items.length==0)return; //会导致编辑界面直接点击保存无响应
        if(!this.validate()){           
            return;
        }
        this.surl = url||this.surl;
        if(this.surl == '') return;
        this.fireBindDataSetEvent("submit",url,items);
        this.lockComponent();
        var p = items;
        for(var i=0;i<p.length;i++){
            var data = p[i]
            //TODO:这里优化,对于更新的数据,只提交更新过的字段,没更新的不提交
            var r = this.findById(data['_id']);
            if(!r) continue;
            var dm = [];
            for(var key in data){
                var pst = true, f = this.fields[key];
                if(f) pst = f.getPro('persisted') !== true;
                if(fields && fields.indexOf(key)==-1 && key !='_id' && key != '_status'){
                    dm[dm.length] = key;
                } else if(!r.isNew && (r.modified == null || r.modified[key] === undefined) && pst && key !='_id' && key != '_status'){                    
                    dm[dm.length] = key;
                } 
//                else {
//                    data[key] = data[key]==='' ? null : data[key];
//                }
            }
            
            for(var j = 0;j<dm.length;j++){
                delete data[dm[j]];
            }
            
            //为什么改成null 可能是 aurora中为了方便处理...不太记得了.
//            for(var key in data){
//                var f = this.fields[key];
//                if(f && f.type != 'dataset' && data[key]==='')data[key]=null;
//            }
            p[i] = Ext.apply(p[i],this.sp)
        }
        $S.request({url:this.surl, para:p, success:success||this.onSubmitSuccess, error:this.onSubmitError, scope:this,failure:this.onAjaxFailed});
    },
    addLock: function(item){
        this.lock = this.lock || [];
        this.lock.concat([].concat(item));        
    },
    lockComponent : function(){
        if(this.loadingTip && Ext.get(this.loadingTip)) Ext.get(this.loadingTip).show();
        if(this.lock){
            for(var i=0;i<this.lock.length;i++){
                var cp = $(this.lock[i]);
                if(cp){
                    if(cp instanceof $S.Window) cp.closeable = false;
                    if(cp instanceof $S.Button) cp.disable();
                }
            }
        }
    },
    releaseComponent : function(){
        if(this.loadingTip && Ext.get(this.loadingTip)) Ext.get(this.loadingTip).setStyle('display','none');//hide();
        if(this.lock){
            for(var i=0;i<this.lock.length;i++){
                var cp = $(this.lock[i]);
                if(cp){
                    if(cp instanceof $S.Window) cp.closeable = true;
                    if(cp instanceof $S.Button) cp.enable();
                }
            }
        }
    },
    isAllReady:function(records){
        for(var i=0;i<records.length;i++){
            if(!records[i].isReady)return false;
        }
        return true;
    },
    /**
     * 提交选中数据.
     * @return {Object} cfg 提交参数.
     */
    submitSelected : function(cfg){
    	var sf=this;
        cfg = cfg||{};
    	var intervalId = setInterval(function(){
            var std = sf.getSelected();
            if(std.length == 0){
                $S.showInfoMessage('提示', '请选择一条数据!');
                clearInterval(intervalId);
                return;
            }
            if(!sf.isAllReady(std)) return;
            clearInterval(intervalId);
            if(sf.fireEvent("beforesubmit",sf)){
                var d = sf.getAllDatas(std);
                sf.doSubmit(cfg.url,d,cfg.fields);
            }
        },10);
    },
    /**
     * 提交数据.
     * @return {Object} cfg 提交参数.
     */
    submit : function(cfg){
        cfg = cfg||{};
    	var sf=this,data = cfg.data||sf.getAll(),intervalId=setInterval(function(){
            if(!sf.isAllReady(data))return;
            clearInterval(intervalId);
            if(sf.fireEvent("beforesubmit",sf)){
                var d = sf.getAllDatas(data);
                sf.doSubmit(cfg.url,[].concat(d),cfg.fields,cfg.success);
            }
        },10);
    },
    /**
     * 重置数据.
     * TODO:update数据的时候把原始数据放到oraValue字段中，reset的时候恢复
     * record上增加reset函数  ds上的循环调用record的reset
     * 触发事件？
     */
    reset : function(){
        var record=this.current();
        if(!record&&!record.fields)return;
        for(var c in record.fields){
            var v=record.fields[c].get('defaultValue');
            if(v!=record.get(c))
                record.set(c,v==undefined||v==null?"":v);
        }
    },
    fireBindDataSetEvent : function(event){
    	var a = Ext.toArray(arguments);
        var event = a[0];
        a[0] = this;
        this.fireEvent.apply(this,[event].concat(a))
        for(var k in this.fields){
            var field = this.fields[k];
            if(field.type == 'dataset'){  
                var ds = field.pro['dataset'];
                if(ds) {
                    ds.fireBindDataSetEvent(event)
                }
            }
        }
    },
    afterEdit : function(record, name, value) {
        this.fireEvent("update", this, record, name, value);
    },
    afterReject : function(record, name, value) {
    	this.fireEvent("reject", this, record, name, value);
    },
    commit : function(datas,fire) {
    	//this.clear();
    	for(var i=0,l=datas.length;i<l;i++){
    		var data = datas[i];
	    	var r = this.findById(data['_id']);
	    	if(r.isNew) this.totalCount ++;
	    	if(!r) return;
	    	r.commit();
	    	for(var k in data){
	    		var field = k;
				var f = this.fields[field];
				if(f && f.type == 'dataset'){
					var ds = f.pro['dataset'];
					ds.reConfig(r.data[f.name]);
	    			if(data[k].record) {
                        ds.commit([].concat(data[k].record), this.current() == r);                     
	    			}
				}else{
					var ov = r.get(field);
					var nv = data[k]
					if(field == '_id' || field == '_status') continue;
					if(f){
					   nv = this.processData(data,k,f);
					}
					if(ov != nv) {
						if(fire){
							//由于commit放到上面,这个时候不改变状态,防止重复提交
                            r.set(field,nv, true);
						}else{
                            r.data[field] = nv;
						}
					}
				}
	       	}
//	       	r.commit();//挪到上面了,record.set的时候会触发update事件,重新渲染.有可能去判断isNew的状态
    	}
    },
    //主要用来处理日期和时间类型,应为json没有日期格式
    processData: function(data, key,field){
        var v = data[key];
        if(v) {
            var dt = field.type;
            dt = dt ? dt.toLowerCase() : '';
            switch(dt){
                case 'date':
                    v = $S.dateFormat.parseDate(v,'isoDate');  
                    break;
                case 'datetime':
                    v = $S.dateFormat.parseDate(v,'isoDateTime');  
                    v.xtype = 'datetime';
                    break;
                case 'int':
                    v = parseInt(v);
                    break;
                case 'float':
                    v = parseFloat(v);
                    break;
                case 'boolean':
                    v = v.toLowerCase() =="true";
                    break;
            }
        }
        //TODO:判断key是否是valueField，然后设置displayField或者displayRenderer的值到对应的name上??
        return this.processValueListField(data,v,field);
    }, 
    processValueListField : function(data,v, field){
        var op = field.getPro('options'),df = field.getPro('displayField'),
            vf = field.getPro('valueField'),rf = field.getPro('returnField'),dr = field.getPro('displayRenderer'),rv = data[rf];
        if(rf && op && rv && !v){
            var options = $(op);
            if(options){
                var r = options.find(vf,rv);
                if(r){
                    var rder = $S.getFunction(dr);
                    if(rder){
                        v = rder.call(window,r);
                    }else{
                        v = r.get(df);
                    }
                }
            }            
        }
        return v;
    },
    onSubmitSuccess : function(res){
        var datas = []
        if(res.result){
            datas = [].concat(res.result);
            this.commit(datas,true)
        }
        this.releaseComponent();
        this.showSuccessTip();
        this.fireBindDataSetEvent('submitsuccess',res);
    },
    showSuccessTip : function(){
        var st = document.getElementById(this.successTip);
        if(st){
            Ext.fly(st).fadeIn();
            setTimeout(function(){
                Ext.fly(st).fadeOut();
            },1500);
        }
    },
    onLoadSuccess : function(res, options){
    	if(res == null) return;
    	if(!res.result) res.result = [];
    	var records = [].concat(res.result);
    	var total = res.total;
    	var datas = [];
    	if(records.length > 0){
    		for(var i=0,l=records.length;i<l;i++){
	    		var item = {
	    			data:records[i]	    		
	    		}
    			datas.push(item);
    		}
    	}else if(records.length == 0){
    		this.currentIndex  = 1
    	}
    	this.loadData(datas, total, options);
    	this.locate(this.currentIndex,true);
        this.loading = false;
    },
    onSubmitError : function(res){
        this.releaseComponent();
        this.fireBindDataSetEvent('submitfailed',res);
    },
    onAjaxFailed : function(res,opt){
        this.releaseComponent();
    	this.fireBindDataSetEvent('ajaxfailed',res,opt);
    },
    onLoadError : function(res,opt){
    	this.fireBindDataSetEvent('loadfailed', res,opt);
        this.loading = false;
    },
    onFieldChange : function(record,field,type,value) {
    	this.fireEvent('fieldchange', this, record, field, type, value)
    },
    onRecordValid : function(record, name, valid){
    	if(valid==false && this.isValid !== false) this.isValid = false;
    	this.fireEvent('valid', this, record, name, valid)
    }
});

/**
 * @class Spark.Record
 * <p>Record是一个数据对象.
 * @constructor
 * @param {Object} data 数据对象. 
 * @param {Array} fields 配置对象. 
 */
$S.Record = function(data){
	/**
     * Record的id. (只读).
     * @type Number
     * @property
     */
    this.id = ++$S.AUTO_ID;
    /**
     * Record的数据 (只读).
     * @type Object
     * @property
     */
    this.data = data;
    /**
     * Record的Fields (只读).
     * @type Object
     * @property
     */
    this.fields = {};
    /**
     * Record的验证信息 (只读).
     * @type Object
     * @property
     */
    this.valid = {};
    /**
     * Record的验证结果 (只读).
     * @type Boolean
     * @property
     */
    this.isValid = true; 
    /**
     * 是否是新数据 (只读).
     * @type Boolean
     * @property
     */
    this.isNew = false;
    /**
     * 是否发生改变 (只读).
     * @type Boolean
     * @property
     */
	this.isDirty = false;	
	/**
     * 编辑状态 (只读).
     * @type Boolean
     * @property
     */
	this.editing = false;
	/**
     * 编辑信息对象 (只读).
     * @type Object
     * @property
     */
	this.modified= null;
    /**
     * 是否是已就绪数据 (只读).
     * @type Boolean
     * @property
     */
    this.isReady=true;
};
$S.Record.prototype = {
	commit : function() {
		this.editing = false;
		this.valid = {};
		this.isValid = true;
		this.isNew = false;
		this.isDirty = false;
		this.modified = null;
	},
	/**
	 * 对当前record的所有field进行校验
	 * @return {boolean} isvalid 校验结果
	 */
	validate : function() {
		this.isValid = true;
		this.valid = {};
		var df = this.ds.fields;
		var rf = this.fields;
		var names = [];
		for(var k in df){
			if(df[k].type !='dataset')
			names.push(k);
		}
		
		for(var k in rf){
			if(names.indexOf(k) == -1){
				if(rf[k].type !='dataset')
				names.push(k);
			}
		}
		for(var i=0,l=names.length;i<l;i++){
			if(this.isValid == true) {
				this.isValid = this.validateField(names[i]);
			} else {
				this.validateField(names[i]);
			}
		}
		return this.isValid;
	},
	/**
	 * 对某个field进行校验.
	 * @param {String} name field的name
	 * @return {boolean} valid 校验结果
	 */
	validateField : function(name){
		var valid = true,v = this.get(name),field = this.getField(name),disabled = field.get('disabled');
        if(disabled == true) return valid;// 如果已经禁用就不需要校验
        
        var vv = v;
        if(v&&v.trim) vv = v.trim();
		if(Ext.isEmpty(vv) && field.get('required') == true){
			this.valid[name] = '当前字段不能为空!';
			valid =  false;
		}
        if(valid == true) {
            var restrict = field.get('restrict'),msg = field.get('validMessage'),validator = field.get('validator'),isvalid = true;
			if(validator){
				validator = window[validator];
				isvalid = validator.call(window,this, name, v);
				if(isvalid !== true){
					valid =	false;	
					this.valid[name] = isvalid;
				}
			}else if(restrict && !new RegExp(restrict).test(v)) {
                valid = false;  
                this.valid[name] = msg||'验证失败!';
            }
		}
		if(valid==true) delete this.valid[name];
		this.ds.onRecordValid(this,name,valid);
		return valid;
	},
    /**
     * 获取field
     * @param {String} name field的name
     * @return {Spark.Record.Field} field 
     */
    getField : function(name){
        if(!name)return null;
        var f = this.fields[name];
        var df = this.ds.fields[name];
        var rf;
        if(!f){
            if(df){
                f = new $S.Record.Field({name:df.name,type:df.type});
            }else{
                f = new $S.Record.Field({name:name});
            }
            f.record = this;
            this.fields[f.name]=f;
        }
  
        var pro = {};
        if(df) pro = Ext.apply(pro, df.pro);
        pro = Ext.apply(pro, f.pro);
        f.snap = pro;
        return f;
    },
    /**
     * 设置值.
     * @param {String} name 设定值的名字.
     * @param {Object} value 设定的值.
     * @param {Boolean} notDirty true 不改变record的isDirty状态.
     */
	set : function(name, value, notDirty){
        if(!$S.equals(this.data[name],value)) {
//        if(!(this.data[name] === value||(Ext.isDate(this.data[name])&&Ext.isDate(value)&&this.data[name].getTime()==value.getTime()))){
            if(!notDirty){
                this.isDirty = true;
                if(!this.modified){
                    this.modified = {};
                }
                if(typeof this.modified[name] == 'undefined'){
                    this.modified[name] = this.data[name] == undefined ? '' : this.data[name];
                }
            }
            this.data[name] = value;// == '' ? null : value; //考虑到mysql的因素,这里把空值转化成null.比较利于数据更新
            if(!this.editing && this.ds) {
                this.ds.afterEdit(this, name, value);
            }
        }
        this.validateField(name)
    },
    /**
     * 设置值.
     * @param {String} name 名字.
     * @return {Object} value 值.
     */
    get : function(name){
        return this.data[name];
    },
    reject : function(silent){
        var m = this.modified;
        for(var n in m){
            if(typeof m[n] != "function"){
                this.data[n] = m[n];
                this.ds.afterReject(this,n,m[n]);
            }
        }
        delete this.modified;
        this.editing = false;
        this.isDirty = false;
    },
    onFieldChange : function(fname, pname, value){
    	var field = this.getField(fname);
    	this.ds.onFieldChange(this, field, pname, value);
    }
}
/**
 * @class Spark.Record.Field
 * <p>Field是一个配置对象，主要配置指定列的一些附加属性，例如非空，只读，值列表等信息.
 * @constructor
 * @param {Object} data 数据对象. 
 */
$S.Record.Field = function(c){
    if(c.type) 
    this.type = c.type;
    this.name = c.name;
    this.pro = c||{};
    this.record;
};
$S.Record.Field.prototype = {
	setPro : function(name,value) {
		var op = this.pro[name];
		if(op !== value){
			this.pro[name] = value;
			if(this.record)this.record.onFieldChange(this.name, name, value);
		}
	},
	getPro : function(name){
		return this.pro[name]
	},
	/**
	 * 获取配置信息
	 * @param {String} name 配置名
	 * @return {Object} value 配置值
	 */
	get : function(name){
		var v = null;
		if(this.snap){
			v = this.snap[name];
		}
		return v;
	},
	/**
	 * 设置当前Field是否必输
	 * @param {Boolean} required  是否必输.
	 */
	setRequired : function(r){
		this.setPro('required',r);
        if(!r && this.record)this.record.validateField(this.name);
	},
	/**
	 * 当前Field是否必输.
	 * @return {Boolean} required  是否必输.
	 */
    isRequired : function(){
        return this.getPro('required');
    },
	/**
	 * 设置当前Field是否禁用.
	 * @param {Boolean} disabled 是否只读
	 */
	setDisabled : function(r){	
		this.setPro('disabled',r);
	},
	/**
	 * 当前Field是否只读.
	 * @return {Boolean} disabled 是否只读
	 */
	isDisabeld : function(){
        return this.getPro('disabled');
	},
	/**
	 * 设置当前Field的值列表.
	 * @param {Object} r 数据集
	 */
	setValueSet : function(v){
		this.setPro('valueset',v);
	},
	/**
     * 获取当前field对应的值列表.
     * @return {Object} r 数据集
     */
	getValueSet : function(){
		return this.getPro('valueset');
	},
	/**
     * 设置当前Field的映射.
     * 例如：<p>
       var mapping = [{from:'name', to: 'code'},{from:'service', to: 'name'}];</p>
       field.setMapping(mapping);
     * @return {Array} mapping 映射列表.
     * 
     */
	setMapping : function(m){
		this.setPro('mapping',m);
	},
	/**
     * 获取当前的映射.
     * @return {Array} array 映射集合
     */
	getMapping : function(){
        return this.getPro('mapping');
	},
	/**
     * 设置Lov弹出窗口的Title.
     * @param {String} title lov弹出窗口的Tile
     */
	setLovTitle : function(t){
		this.setPro('title',t);
	},
	/**
     * 设置Lov弹出窗口的宽度.
     * @param {Number} width lov弹出窗口的Width
     */
	setLovWidth : function(w){
        this.setPro('lovwidth',w);
	},
	/**
     * 设置Lov弹出窗口的高度.
     * @param {Number} height lov弹出窗口的Height
     */
	setLovHeight : function(h){
		this.setPro('lovheight',h);
	},
	/**
     * 设置Lov弹出窗口中grid的高度.
     * 配置这个主要是由于查询条件可能存在多个，导致查询的form过高.
     * @param {Number} height lov弹出窗口的grid组件的Height
     */
	setLovGridHeight : function(gh){
        this.setPro("lovgridheight",gh)
	},
//	/**
//     * 设置Lov的Model对象.
//     * Lov的配置可以通过三种方式.(1)model (2)service (3)url.
//     * @param {String} model lov配置的model.
//     */
//	setLovModel : function(m){
//        this.setPro("lovmodel",m) 
//	},
//	/**
//     * 设置Lov的Service对象.
//     * Lov的配置可以通过三种方式.(1)model (2)service (3)url.
//     * @param {String} service lov配置的service.
//     */
//	setLovService : function(m){
//        this.setPro("lovservice",m) 
//    },
//    /**
//     * 设置Lov的Url地址.
//     * Lov的配置可以通过三种方式.(1)model (2)service (3)url.
//     * 通过url打开的lov，可以不用调用setLovGridHeight
//     * @param {String} url lov打开的url.
//     */
//    setLovUrl : function(m){
//    	this.setPro("lovurl",m) 
//    },
    setLovPara : function(name,value){
        var p = this.getPro('lovpara');
        if(!p){
            p = {};
            this.setPro("lovpara",p) 
        }
        if(value==null){
        	delete p[name]
        }else{
            p[name] = value;
        }
    }
	
}
/**
 * @class Spark.Component
 * @extends Ext.util.Observable
 * <p>所有组件对象的父类.
 * <p>所有的子类将自动继承Component的所有属性和方法.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Component = Ext.extend(Ext.util.Observable,{
    constructor: function(config) {
        $S.Component.superclass.constructor.call(this);
        this.isHidden = false;
        this.isFireEvent = false;
        this.config=config;
        this.initComponent(config);
        this.initEvents();
        this.initBind();
        this.initStatus();
        if(config.register !== false) $S.CmpManager.put(this.id,this)
    },
    initConfig : function(config){
        config = config || {};
        this.id = config.id || Ext.id();
        Ext.apply(this, config);
    },
    initBind : function(){
        if(this.bt && this.name) this.bind(this.bt,this.name)
    },
    initComponent : function(config){
        this.initConfig(config);
        if(this.listeners){
            this.on(this.listeners);
        }
//        this.originalValue = this.getValue();
    },
    processListener: function(ou){},
    initStatus : function(){},
    initEvents : function(){
        this.addEvents(
        'change',
        /**
         * @event valid
         * 组件验证事件.
         * @param {Component} this 当前组件.
         * @param {Spark.Record} record record对象.
         * @param {String} name 对象绑定的Name.
         * @param {Boolean} isValid 验证是否通过.
         */
        'valid',
        /**
         * @event bind
         * bind事件.
         * @param {Component} this 当前组件.
         * @param {Spark.Record} record record对象.
         */
        'bind');
        this.processListener('on');
    },
    /**
     * 获取绑定的name
     * @return {String} 绑定的name
     */
    getBindName: function(){
        return this.binder ? this.binder.name : null;
    },
    /**
     * 获取绑定的DataSet
     * @return {Spark.DataSet} ds
     */
    getBindDataSet: function(){
        return this.binder ? this.binder.ds : null;
    },
    /**
     * 将组件绑定到某个DataSet的某个Field上.
     * @param {Spark.DataSet} dataSet 绑定的DataSet. 可以是具体某个DataSet对象，也可以是某个DataSet的id.
     * @param {String} name Field的name. 
     */
    bind : function(ds, name){
        this.clearBind();
        if(typeof(ds) == 'string'){
            ds = $(ds);
        }
        if(!ds)return;
        this.binder = {
            ds: ds,
            name:name
        }
        this.record = ds.current();
        var field =  ds.fields[this.binder.name];
        if(field) {
            var config={};
            Ext.apply(config,this.config);
            Ext.apply(config, field.pro);
            delete config.name;
            delete config.type;
            this.initComponent(config);  
            this.initStatus();
        }
        ds.on('metachange', this.onRefresh, this);
        ds.on('valid', this.onValid, this);
        ds.on('remove', this.onRemove, this);
        ds.on('clear', this.onClear, this);
        ds.on('update', this.onUpdate, this);
        ds.on('reject', this.onUpdate, this);
        ds.on('fieldchange', this.onFieldChange, this);
        ds.on('indexchange', this.onRefresh, this);
        this.onRefresh(ds)
        this.fireEvent('bind', this, this.record);
    },
    /**
     * 清除绑定.
     * <p>删除所有绑定的事件信息.
     */
    clearBind : function(){
        if(this.binder) {
            var bds = this.binder.ds;
            bds.un('metachange', this.onRefresh, this);
            bds.un('valid', this.onValid, this);
            bds.un('remove', this.onRemove, this);
            bds.un('clear', this.onClear, this);
            bds.un('update', this.onUpdate, this);
            bds.un('reject', this.onUpdate, this);
            bds.un('fieldchange', this.onFieldChange, this);
            bds.un('indexchange', this.onRefresh, this);
        } 
        this.binder = null; 
        this.record = null;
    },
    /**
     * 设置当前的值.
     * @param {Object} value 值对象
     * @param {Boolean} silent 是否更新到dataSet中
     */
    setValue : function(v, silent){
        var ov = this.value;
        this.value = v;
        if(silent === true)return;
        if(this.binder){
            this.record = this.binder.ds.current();
            if(this.record == null){
                this.record = this.binder.ds.create({},false);                
            }
            this.record.set(this.binder.name,v);
            if(Ext.isEmpty(v,true)) delete this.record.data[this.binder.name];
        }
        if(ov!=v){
            this.fireEvent('change', this, v, ov);
        }
    },
    /**
     * 返回当前值
     * @return {Object} value 返回值.
     */
    getValue : function(){
        var v = this.value;
        v = (v === null || v === undefined ? '' : v);
        return v;
    },
//    /**
//     * 重置  通过dataset来reset
//     */
//    reset : function(){
//        this.setValue(this.originalValue);
//    },
    /**
     * <p>销毁组件对象.</p>
     * <p>1.删除所有绑定的事件.</p>
     * <p>2.从对象管理器中删除注册信息.</p>
     * <p>3.删除dom节点.</p>
     */
    destroy : function(){
        this.processListener('un');
        $S.CmpManager.remove(this.id);
        this.clearBind();
    },
    render : function(record){
        this.record = record;
        if(this.record) {
            var value = this.record.get(this.binder.name);
            var field = this.record.getField(this.binder.name);     
            var config={};
            Ext.apply(config,this.config);
            Ext.apply(config, field.snap);
            this.initComponent(config);
            this.initStatus();
//          if(this.record.valid[this.binder.name]){
//              this.markInvalid();
//          }
            //TODO:和lov的设值有问题
//          if(this.value == value) return;
            if(!Ext.isEmpty(value,true)) {
                this.setValue(value,true);
            }else{
                this.clearValue();
            }
        } else {
            this.setValue('',true);
        }
    },
    onRemove : function(ds, record){
        if(this.binder.ds == ds && this.record == record){
            this.clearValue();
        }
    },
    onRefresh : function(ds){
        if(this.isFireEvent == true || this.isHidden == true) return;
        this.clearInvalid();
        this.render(ds.current());
    },
    onValid : function(ds, record, name, valid){
        if(this.binder.ds == ds && this.binder.name == name && this.record == record){
            if(valid){
                this.fireEvent('valid', this, this.record, this.binder.name, true)
                this.clearInvalid();
            }else{
                this.fireEvent('valid', this, this.record, this.binder.name, false);
                this.markInvalid();
            }
        }       
    },
    onUpdate : function(ds, record, name, value){
        if(this.binder.ds == ds && this.record == record && this.binder.name == name && this.getValue() !== value){
            this.setValue(value, true);
        }
    },
    onFieldChange : function(ds, record, field){
        if(this.binder.ds == ds && this.record == record && this.binder.name == field.name){
            this.onRefresh(ds);     
        }
    },
    onClear : function(ds){
        this.clearValue();    
    },
    clearInvalid : function(){},
    markInvalid : function(){},
    clearValue : function(){},
    initMeta : function(){},
    setDefault : function(){},
    setRequired : function(){},
    onDataChange : function(){}
});
/**
 * @class Spark.ViewComponent
 * @extends Spark.Component
 * <p>可视化的组件,可改变尺寸，具有focus,blur,mouseover,mouseout事件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.ViewComponent = Ext.extend($S.Component,{    
    disabledCss:'s_dbd',
    isFocused : false,
    constructor: function(config) {
        $S.ViewComponent.superclass.constructor.call(this, config);
        if(this.hidden == true){
            this.setVisible(false)
        }
    },
    initConfig : function(config){
        config.disabled = config.disabled || false;
        $S.ViewComponent.superclass.initConfig.call(this, config);
    },
    initFocusElement : function(){
        this.focusEl = this.wrap;
    },
    initStatus : function(){
        this.initDisabled(this.disabled);
    },
    initComponent : function(config){
        $S.ViewComponent.superclass.initComponent.call(this,config);
        this.wrap = Ext.get(this.id);
    },
    doDisabled :function(disabled){
        if(disabled){
            this.wrap.addClass(this.disabledCss);
        }else{
            this.wrap.removeClass(this.disabledCss);
        }
    },
    initDisabled : function(disabled){
        if(this.isDisabled == disabled)return;
        this.clearInvalid();
        this.isDisabled = disabled;
        this.doDisabled(disabled);
    },
    initEvents : function(){
        this.initFocusElement();
        $S.ViewComponent.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event focus
         * 获取焦点事件
         * @param {Component} this 当前组件.
         */
        'focus',
        /**
         * @event blur
         * 失去焦点事件
         * @param {Component} this 当前组件.
         */
        'blur',
        /**
         * @event mouseover
         * 鼠标经过组件事件.
         * @param {Component} this 当前组件.
         * @param {EventObject} e 鼠标事件对象.
         */
        'mouseover',
        /**
         * @event mouseout
         * 鼠标离开组件事件.
         * @param {Component} this 当前组件.
         * @param {EventObject} e 鼠标事件对象.
         */
        'mouseout');
    },
    processListener: function(ou){
        $S.ViewComponent.superclass.processListener.call(this,ou);
        this.processMouseOverOut(ou);
        this.processFocusBlurListener(ou);
        if(!Ext.isEmpty(this.marginWidth)||!Ext.isEmpty(this.marginHeight)) {
            Ext.EventManager[ou](window, "resize", this.windowResizeListener,this);
        }
    },
    processFocusBlurListener: function(ou){
        this.focusEl[ou]("focus", this.onFocus,  this)
                    [ou]("blur", this.onBlur,  this)
                    [ou]("keydown", this.onFocusKeyDown,  this);
    },
    processMouseOverOut : function(ou){
        this.wrap[ou]("mouseover", this.onMouseOver, this)
                 [ou]("mouseout", this.onMouseOut, this);
    },
    /**
     * 设置组件是否可见
     * @param {Boolean} v 是否可见
     */
    setVisible: function(v){
        if(v==true)
//            this.wrap.show();
            this.wrap.setStyle('display','');
        else
//            this.wrap.hide();
            this.wrap.setStyle('display','none');
    },
    /**
     * 设置组件宽度
     * @param {Number} w 宽度,单位px
     */
    setWidth: function(w){
        this.width = w;
        this.wrap.setWidth(w);
    },
    /**
     * 设置组件高度
     * @param {Number} h 高度,单位px
     */
    setHeight: function(h){
        this.height = h;
        this.wrap.setHeight(h);
    },
    /**
     * 组件获取焦点
     */
    focus : function(){
        if(this.isFocused) return;
        this.focusEl.focus();
    },
    /**
     * 组件失去焦点
     */
    blur : function(){
        if(!this.isFocused) return;
        this.focusEl.blur();
    },
    /**
     * 移动组件到指定位置
     * @param {Number} x X坐标
     * @param {Number} y Y坐标
     */
    move: function(x,y){
        this.wrap.setX(x);
        this.wrap.setY(y);
    },
    windowResizeListener : function(){
        var w,h;
        if(!Ext.isEmpty(this.marginWidth)){
            var wd = Spark.getViewportWidth();
            w = wd-this.marginWidth;
            this.setWidth(w);
        }
        if(!Ext.isEmpty(this.marginHeight)){
            var ht = Spark.getViewportHeight();
            h = ht-this.marginHeight; 
            this.setHeight(h);
        }
    },
    isEventFromComponent:function(el){
        return this.wrap.contains(el)
    },
    destroy : function(){
        $S.ViewComponent.superclass.destroy.call(this);
        delete this.wrap;
    },
    onFocusKeyDown : function(e){},
    onFocus : function(e){
        if(!this.isFocused){
            this.isFocused = true;            
            this.fireEvent("focus", this);
        }
    },
    onBlur : function(e){
        if(this.isFocused){
            this.isFocused = false;            
            this.fireEvent("blur", this);
        }
    },
    onMouseOver : function(e){
        this.fireEvent('mouseover', this, e);
    },
    onMouseOut : function(e){
        this.fireEvent('mouseout', this, e);
    }
})
/**
 * @class Spark.QueryForm
 * @extends Spark.ViewComponent
 * <p>QueryForm组件.
 * @author njq.niu@hand-china.com
 * @constructor 
 */
$S.QueryForm = Ext.extend($S.ViewComponent,{
    isExpanded : false,
    initComponent : function(cfg){
        $S.QueryForm.superclass.initComponent.call(this, cfg);
        this.child = cfg.child||[];
        this.inner = this.wrap.child('div.s_qf_w');
        this.body = Ext.get(this.id+'_body');
        this.shadow = this.wrap.child('div[stype=shadow]');
        this.menuBtn = $(cfg.queryButton);
    },
    initEvents : function(){
        $S.QueryForm.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event expand
         * expand事件.
         * @param {Component} this 当前组件.
         */
        'expand',
        /**
         * @event collapse
         * collapse事件.
         * @param {Component} this 当前组件.
         */
        'collapse');
        this.processListener('on');
    },
    processListener: function(ou){
        $S.QueryForm.superclass.processListener.call(this,ou);
        if(this.menuBtn){
            this.menuBtn[ou]('expand',this.expand,this);
            this.menuBtn[ou]('collapse',this.collapse,this);
        }
    },
    triggerBlur : function(e,t){
        if(!this.isEventFromComponent(t)){    
            this.collapse();
            this.menuBtn.trbtn.removeClass('up');
            this.menuBtn.trbtn.addClass('down');  
        }
    },
    isEventFromComponent:function(el){
        var v = $S.QueryForm.superclass.isEventFromComponent.call(this,el);
        if(!v)
        Ext.each(this.child,function(id){
            var cmp = $(id);
            if(cmp && cmp.isEventFromComponent){
                if(cmp.isEventFromComponent(el)){
                    v = true;
                    return true;
                }
            }
        })
        return v;
    },
    /**
     * 折叠弹出面板
     */
    collapse : function(){
        if(!this.isExpanded) return;
        Ext.get(document.documentElement).un("mousedown", this.triggerBlur, this);
        this.body.setStyle('display', 'none');
        this.inner.removeClass('s_qf_w_s');
        this.inner.setStyle({'position':'','z-index':'','top':'0px','left':'0px','border':'none'});
        this.shadow.moveTo(-1000,-1000);
        this.isExpanded = false;
    },
    /**
     * 展开弹出面板
     */
    expand : function(){
        Ext.get(document.documentElement).un("mousedown", this.triggerBlur, this).on("mousedown", this.triggerBlur, this);
        var xy = this.wrap.getXY();
        this.body.setStyle('display', '');
        this.inner.addClass('s_qf_w_s');
        this.inner.setStyle({'position':'absolute','z-index':'9990','border':'1px solid #ccc'});
        this.inner.moveTo(xy[0]-1, xy[1]-1);
        this.shadow.setWidth(this.inner.getWidth());
        this.shadow.setHeight(this.inner.getHeight());
        this.shadow.moveTo(xy[0]-1, xy[1]-1);
        this.isExpanded = true;
    }
})
/**
 * @class Spark.Field
 * @extends Spark.Component
 * <p>带有input标记的输入类的组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Field = Ext.extend($S.ViewComponent,{    
    validators: [],
    focusCss:'s_fs',
    requiredCss:'s_rq',
    placeholderCss:'s_et',
    invalidCss:'s_ivd',
    constructor: function(config) {
        config.required = config.required || false;
        $S.Field.superclass.constructor.call(this, config);
//        this.initValue(this.originalValue);
        this.applyPlaceholder();
    },
    initFocusElement : function(){
        this.focusEl = this.el;
    },
    initComponent : function(config){
        $S.Field.superclass.initComponent.call(this, config);
        this.el = this.wrap.child('input[stype=input]'); 
    },
//    initValue : function(v){},
    initStatus : function(){
        this.clearInvalid();
        this.initRequired(this.required);
        this.initMaxLength(this.maxLength);
        $S.Field.superclass.initStatus.call(this);
    },
    initRequired : function(required){
        if(this.crrentRequired == required)return;
        this.clearInvalid();        
        this.crrentRequired = required;
        if(required){
            this.wrap.addClass(this.requiredCss);
        }else{
            this.wrap.removeClass(this.requiredCss);
        }
    },
    initMaxLength : function(ml){
        if(ml) this.el.dom.maxLength=ml;
    },
    initDisabled : function(disabled){
        $S.Field.superclass.initDisabled.call(this,disabled);
        this.el.dom.readOnly = disabled;
    },
    initEvents : function(){
        $S.Field.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event change
         * change事件.
         * @param {Spark.Field} field field对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'change',
        /**
         * @event keydown
         * 键盘按下事件.
         * @param {Spark.Field} field field对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'keydown',
        /**
         * @event keyup
         * 键盘抬起事件.
         * @param {Spark.Field} field field对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'keyup',
        /**
         * @event keypress
         * 键盘敲击事件.
         * @param {Spark.Field} field field对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'keypress',
        /**
         * @event enterdown
         * 回车键事件.
         * @param {Spark.Field} field field对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'enterdown');
    },
    processListener: function(ou){
        $S.Field.superclass.processListener.call(this, ou);
        this.el[ou]("change",   this.onChange, this)
               [ou]("keyup",    this.onKeyUp, this)
               [ou]("keydown",  this.onKeyDown, this)
               [ou]("keypress", this.onKeyPress, this)
               [ou]("mouseup",  this.onMouseUp, this);
    },
    destroy : function(){
        $S.Field.superclass.destroy.call(this);
        delete this.el;
    },
    setWidth: function(w){
        this.wrap.setStyle("width",(w-2)+"px");//左右边框为2
        this.el.setStyle("width",(w-7)+"px");//paddingleft + leftborder + rightborder
    },
    setHeight: function(h){
        this.wrap.setStyle("height",(h-2)+"px");//上下边框是2
        this.el.setStyle("height",(h-4)+"px");//上下边框2，上下padding是2
    },
    processKeyDown : function(e){
        var keyCode = e.keyCode;
        if(this.isEditor==true && keyCode == 9) e.stopEvent();
        if(keyCode == 27) {
            e.stopEvent();
        }
    },
    processValue : function(v){
        return v;
    },
    processMaxLength : function(rv){
        if(this.isOverMaxLength(rv)){
            var sb = [];
            for (i = 0,k=0; i < rv.length;i++) {
                var cr = rv.charAt(i);
                var cl = cr.match(/[^\x00-\xff]/g);
                if (cl !=null && cl.length>0) {
                    k=k+$S.defaultChineseLength;
                } else {
                    k=k+1
                }
                if(k<=this.maxLength) {
                    sb[sb.length] = cr
                }else{
                    break;
                }
            }
            return sb.join('');
        }
        return rv;
    },  
    isOverMaxLength : function(str){
        if(!this.maxLength) return false;
        var c = 0;
        for (i = 0; i < str.length; i++) {
            var cr = str.charAt(i);
            var cl = cr.match(/[^\x00-\xff]/g);
            if (cl !=null &&cl.length >0) {
                c=c+$S.defaultChineseLength;
            } else {
                c=c+1;
            }
        }
        return c > this.maxLength;
    },
    setValue : function(v, silent){
        $S.Field.superclass.setValue.call(this,v, silent);
        if(this.placeholder && this.el && v !== undefined && v !== null && v !== ''){
            this.wrap.removeClass(this.placeholderCss);
        }
        this.setRawValue(this.formatValue((v === null || v === undefined ? '' : v)));
        this.applyPlaceholder();
    },
    formatValue : function(v){
        var rder = (this.renderer) ? $S.getFunction(this.renderer) : null;
        return (rder!=null) ? rder.call(window,v) : v;
    },
    getRawValue : function(){
        var v = this.el.getValue(),typecase=this.inputCase;
        if(v === this.placeholder || v === undefined){
            v = '';
        }
        if(this.isDbc(v)){
            v = this.dbc2sbc(v);
        }
        if(typecase){
            if(typecase == 'upper'){
                v = v.toUpperCase();
            }else if(typecase == 'lower') {
                v = v.toLowerCase();
            }
        }
        return v;
    },
    applyPlaceholder : function(){
        if(this.placeholder && this.getRawValue().length < 1){
            this.setRawValue(this.placeholder);
            this.wrap.addClass(this.placeholderCss);
        }
    },
    clearInvalid : function(){
        this.wrap.dom.removeAttribute('data-tooltip');
        this.wrap.dom.removeAttribute('data-tooltip-style');
        this.wrap.removeClass(this.invalidCss);
    },
    markInvalid : function(msg){
        if(!this.record)return;
        var field = this.record.getField(this.binder.name)
        var msg = this.record.valid[this.binder.name] || field.get('tooltip');
        this.wrap.set({
            'data-tooltip':msg,
            'data-tooltip-type':'tip',
            'data-tooltip-style':'valid'
        })
        this.wrap.addClass(this.invalidCss);
    },
    select : function(start, end){
        var v = this.getRawValue();
        if(v.length > 0){
            start = start === undefined ? 0 : start;
            end = end === undefined ? v.length : end;
            var d = this.el.dom;
            if(d.setSelectionRange){  
                d.setSelectionRange(start, end);
            }else if(d.createTextRange){
                var range = d.createTextRange();
                range.moveStart("character", start);
                range.moveEnd("character", end-v.length);
                range.select();
            }
        }
        this.isSelect = true;
    },
    setRawValue : function(v){
        if(this.el.dom.value === (v === null || v === undefined ? '' : v)) return;
        this.el.dom.value = (v === null || v === undefined ? '' : v);
    },
//    通过dataset来reset
//    reset : function(){
//        $S.Field.superclass.reset.call(this);
//        this.clearInvalid();
//        this.applyPlaceholder();
//    },
    clearValue : function(){
        this.setValue('', true);
        this.clearInvalid();
        this.applyPlaceholder();
    },
    /**
     * 是否是全角字符
     * @param {} s
     * @return {}
     */
    isDbc : function(s){
        var dbc = false;
        for(var i=0;i<s.length;i++){
            var c = s.charCodeAt(i);
            if((c>65248)||(c==12288)) {
                dbc = true
                break;
            }
        }
        return dbc;
    },
    /**
     * 全角转半角
     * @param {} str
     * @return {}
     */
    dbc2sbc : function(str){
        var result = '';
        for(var i=0;i<str.length;i++){
            code = str.charCodeAt(i);//获取当前字符的unicode编码
            if (code >= 65281 && code <= 65373) {//在这个unicode编码范围中的是所有的英文字母已及各种字符
                result += String.fromCharCode(str.charCodeAt(i) - 65248);//把全角字符的unicode编码转换为对应半角字符的unicode码                
            } else if (code == 12288){//空格
                result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
            } else {
                result += str.charAt(i);
            }
        }
        return result;
    },
    onChange : function(e){
        this.fireEvent('change', this, e);
    },
    onKeyUp : function(e){
        this.fireEvent('keyup', this, e);
    },
    onKeyDown : function(e){
        this.fireEvent('keydown', this, e);
        this.processKeyDown(e);
    },
    onKeyPress : function(e){
        this.fireEvent('keypress', this, e);
    },
    onFocus : function(e){
        if(!this.isFocused){
            this.startValue = this.getValue();
            if(this.placeholder){
                if(this.el.dom.value == this.placeholder){
                    this.setRawValue('');
                }
                this.wrap.removeClass(this.placeholderCss);
            }
//            this.select();
            this.wrap.addClass(this.focusCss);
            $S.Field.superclass.onFocus.call(this,e);
        }
    },
    onBlur : function(e){
        if(this.isFocused){
            var rv = this.getRawValue();
            rv = this.processMaxLength(rv);
            rv = this.processValue(rv);
            this.setValue(rv);
            this.wrap.removeClass(this.focusCss);
            $S.Field.superclass.onBlur.call(this,e);
        }
    },
    onMouseUp : function(e){
        if(this.isSelect) e.stopEvent();
        this.isSelect = false;
    }
})
$S.Menu = Ext.extend($S.ViewComponent,{
    hoverCls:'s-menuitem-hover',
    align:"r",
    constructor: function(config) {
        $S.Menu.superclass.constructor.call(this, config);
        this.align = config.align||this.align;
    },
    initComponent : function(config){
        $S.Menu.superclass.initComponent.call(this, config);
        Ext.getBody().insertFirst(this.wrap);
        this.initMenuElement();
    },
    initEvents : function(){
        $S.Button.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event click
         * 鼠标点击事件.
         * @param {Spark.Menu} menu对象.
         * @param {String} mid mid.
         * @param {EventObject} e 键盘事件对象.
         */
        'click');
    },
    initMenuElement: function(){
        for(var key in this.map){
            var elid = this.map[key];
//            Ext.getBody().insertFirst(elid);
            Ext.get(elid).insertAfter(this.wrap)
        }
    },
    processListener: function(ou){
        $S.Menu.superclass.processListener.call(this, ou);
    },
    destroy : function(){
        $S.Menu.superclass.destroy.call(this);
        this.clearLisener();
    },
    onDocumentMouseOver: function(e){
        var menu = this.findTargetDom(e.target,'role','menu');
        if(!menu) return;
        if(Ext.fly(menu).contains(e.target)) {
            var menuItem = this.findTargetDom(e.target,'role','menuitem');
            this.hover(menu,menuItem);
        }        
    },
    isEventFromComponent:function(el){
        var v = $S.Menu.superclass.isEventFromComponent.call(this,el);
        if(!v) {
            for(var key in this.map){
                v = Ext.fly(this.map[key]).contains(el);
                if(v) return true;
            }
            Ext.each(this.child,function(id){
                var cmp = $(id);
                if(cmp && cmp.isEventFromComponent){
                    if(cmp.isEventFromComponent(el)){
                        v = true;
                        return true;
                    }
                }
            })
        }
        return v;
    },
    findTargetDom: function(target,type,value){
        var v = Ext.fly(target).getAttributeNS('',type);
        if(v==value) {
            return target;
        }else {
            var el = Ext.fly(target).parent('['+type+'='+value+']');
            return el&&el.dom;
        }
        
    },
    findHoverItem : function(el){
        return Ext.fly(el).child('div.'+ this.hoverCls);
    },
    hover: function(menu,menuItem){
        var hoverItem = this.findHoverItem(menu);
        if(hoverItem && menuItem == hoverItem.dom) return;
        this.unhover(menu);
        if(menuItem) {
            Ext.fly(menuItem).addClass(this.hoverCls);
            var mid = Ext.fly(menuItem).getAttributeNS('','mid');
            if(this.map[mid]){
                this.openSubMenu(menuItem,mid);
            }
            Ext.each(this.child,function(id){
                var cmp = $(id);
                if(cmp && cmp.collapse){
                    cmp.collapse()
                }
            })
        }
        
    },
    unhover: function(menu){
        var hoverItem = this.findHoverItem(menu);
        if(hoverItem) {
            Ext.fly(hoverItem).removeClass(this.hoverCls);
            var mid = Ext.fly(hoverItem).getAttributeNS('','mid');
            if(this.map[mid]){
                this.closeSubMenu(mid);
            }
        }
    },
    initLisener: function(){
        Ext.get(document.documentElement).on("mouseover", this.onDocumentMouseOver, this);
        Ext.get(document.documentElement).on("click", this.onDocumentClick, this);
    },
    clearLisener: function(){
        Ext.get(document.documentElement).un("mouseover", this.onDocumentMouseOver, this);
        Ext.get(document.documentElement).un("click", this.onDocumentClick, this);
    },
    onDocumentClick: function(e){
        var menuItem = this.findTargetDom(e.target,'role','menuitem');
        if(!menuItem) return;
        var mid = Ext.fly(menuItem).getAttributeNS('','mid');
        if(!this.map[mid]){
            this.fireEvent("click", this,mid, e);
            this.hide();
        }
    },
    openSubMenu: function(dom,id){
        var menuItem = Ext.get(dom),menu = Ext.get(this.map[id]);
        menu.dom.style.opacity=1;
//        menu.setDisplayed('block');
        this.syncSubMenu(menuItem,menu);
    },
    syncSubMenu : function(menuItem,menu){
        var vs = (Ext.isIE6 || Ext.isIE7) ? 2 : 0, 
            sl = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollLeft,
            st = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollTop,
            xy = menuItem.getXY(),_x = xy[0] - sl,_y = xy[1] - st,
            W=menu.getWidth(),H=menu.getHeight(),
            PH=menuItem.getHeight(),PW=menuItem.getWidth(),
            BH=$S.getViewportHeight()-3,BW=$S.getViewportWidth()-3,
            x = this.align == 'r' ? ((_x+W+PW)>BW ? (_x-W+vs):(_x+PW - vs)) : ((_x-W+vs)>0 ? (_x-W+vs) : (_x+PW - vs)),
            y = (_y+H)>BH ? (_y+PH-H) : _y;
        menu.setLeft(x+sl);
        menu.setTop(y+st);
    },
    closeSubMenu: function(id){
        var menu = Ext.get(this.map[id]);
        menu.dom.style.opacity=0;
        menu.moveTo(-10000,-10000);
//        menu.setDisplayed('none');
        this.unhover(menu);      
    },
    show: function(x,y){
        this.wrap.setLeft(x);
        this.wrap.setTop(y);
        this.wrap.dom.style.opacity=1;
//        this.wrap.setDisplayed('block');
        this.initLisener();
    },
    hide: function(){
        this.unhover(this.wrap);
        this.wrap.moveTo(-10000,-10000);
        this.wrap.dom.style.opacity=0;
//        this.wrap.setDisplayed('none');
        for(var key in this.map){
            var m = Ext.fly(this.map[key]);
            m.moveTo(-10000,-10000);
            m.dom.style.opacity=0;
//            m.setDisplayed('none');
        }
        this.clearLisener();
    }
})   
/**
 * @class Spark.ImageCode
 * @extends Spark.Component
 * <p>图片验证码组件.
 * @author njq.niu@hand-china.com
 * @constructor 
 */
$S.ImageCode = Ext.extend($S.ViewComponent,{
    processListener: function(ou){
        $S.ImageCode.superclass.processListener.call(this,ou);
        this.wrap[ou]("click", this.onClick,  this);
    },
    onClick : function(){
    	this.refresh();
    },
    /**
     * 重新加载验证码
     * 
     */
    refresh : function(){
        this.wrap.dom.src = "imagecode?r="+Math.random();
    }
});
/**
 * @class Spark.Label
 * @extends Spark.Component
 * <p>Label组件.
 * @author njq.niu@hand-china.com
 * @constructor 
 */
$S.Label = Ext.extend($S.ViewComponent,{
    onUpdate : function(ds, record, name, value){
    	if(this.binder.ds == ds && this.binder.name == name){
	    	this.updateLabel(record,name,value);
    	}
    },
    /**
     * 绘制Label
     * @param {Spark.Record} record record对象
     */
    render : function(record){
    	this.record = record;
    	if(this.record) {
			var value = this.record.get(this.binder.name);
			this.updateLabel(this.record,this.binder.name,value);
    	}
    },
    updateLabel: function(record,name,value){
        var rder = $S.getFunction(this.renderer);
	    if(rder!=null){
    		value = rder.call(window,value,record, name);
	    }
	    this.wrap.update(value);
    }
});
/**
 * @class Spark.Button
 * @extends Spark.Component
 * <p>按钮组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Button = Ext.extend($S.ViewComponent,{
    disableCss:'s-btn-disabled',
    overCss:'s-btn-over',
    pressCss:'s-btn-pressed',
    disabled:false,
    constructor: function(config) {
        $S.Button.superclass.constructor.call(this, config);
    },
    initComponent : function(config){
        $S.Button.superclass.initComponent.call(this, config);
        this.text = this.wrap.child('span[stype=text]');
        if(this.hidden == true)this.setVisible(false)
        if(this.disabled == true)this.disable();
    },
    processListener: function(ou){
        $S.Button.superclass.processListener.call(this,ou);
        this.wrap[ou]("click", this.onClick,  this);
        this.wrap[ou]("focus",this.onFocus,this);
        this.wrap[ou]("blur",this.onBlur,this);
        this.wrap[ou]("mousedown", this.onMouseDown,  this);
    },
    initEvents : function(){
        $S.Button.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event click
         * 鼠标点击事件.
         * @param {Spark.Button} button 按钮对象.
         * @param {EventObject} e 键盘事件对象.
         */
        'click');
    },
    /**
     * 获取焦点
     */
    focus: function(){
        if(this.disabled)return;
        this.wrap.dom.focus();
    },
    /**
     * 失去焦点
     */ 
    blur : function(){
        if(this.disabled) return;
        this.wrap.dom.blur();
    },
    /**
     * 设置不可用状态
     */
    disable: function(){
        this.disabled = true;
        this.wrap.removeClass([this.pressCss])
        this.wrap.addClass(this.disableCss);
        this.wrap.dom.disabled = true;
    },
    /**
     * 设置可用状态
     */
    enable: function(){
        this.disabled = false;
        this.wrap.removeClass(this.disableCss);
        this.wrap.dom.disabled = false;
    },
    setEnabled : function(d){
        if(d===true) this.enable()
        else this.disable()
    },
    /**
     * 设置按钮文字
     */
    setText: function(t){
        if(t)this.text.update(t)
    },
    onMouseDown: function(e){
        e.preventDefault();
        this.focus();
        if(!this.disabled){
            this.wrap.addClass(this.pressCss);
            Ext.get(document.documentElement).on("mouseup", this.onMouseUp, this);
        }
    },
    onMouseUp: function(e){
        if(!this.disabled){
            Ext.get(document.documentElement).un("mouseup", this.onMouseUp, this);
            this.wrap.removeClass(this.pressCss);
        }
    },
    onFocusKeyDown : function(e){
        var key = e.getKey();
        if(key == 13 || key == 32){
            e.stopEvent();
            this.onClick(e);
        }else if(key ==27){
            this.onBlur(e)
        }
    },
    onClick: function(e){
        if(!this.disabled){
            this.fireEvent("click", this, e);
        }
    },
    onFocus : function(e){
        this.isFocused = true;
        this.onMouseOver(e);
    },
    onBlur : function(e){
        this.isFocused = false;
        this.onMouseOut(e)
    },
    onMouseOver: function(e){
        $S.Button.superclass.onMouseOver.call(this,e);
        if(!this.disabled)
            this.wrap.addClass(this.overCss);
       
    },
    onMouseOut: function(e){
        $S.Button.superclass.onMouseOut.call(this,e);
        if(!this.disabled)
            this.wrap.removeClass(this.overCss);
    }
});
$S.Button.getTemplate = function(id,text,width,css){
    width = width||70;
    return '<div style="width:'+width+'px; height: 25px; line-height: 25px;" id="'+id+'" class="s-btn '+(css||'')+'"><span class="e" unselectable="on">&nbsp;</span><span id="ext-gen40" unselectable="on">'+text+'</span></div><script>new Spark.Button({"id":"'+id+'"});</script>';
}
/**
 * @class Spark.XButton
 * @extends Spark.Component
 * <p>按钮组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.XButton = Ext.extend($S.ViewComponent,{
	disableCss:'s_disabled',
	overCss:'s_btn-over',
	pressCss:'s_btn-pressed',
	disabled:false,
	constructor: function(config) {
        $S.XButton.superclass.constructor.call(this, config);
    },
    initComponent : function(config){
        $S.XButton.superclass.initComponent.call(this, config);
        this.el = this.wrap.child('button[stype=btn]');
        if(this.hidden == true)this.setVisible(false)
        if(this.disabled == true)this.disable();
    },
    processListener: function(ou){
    	$S.XButton.superclass.processListener.call(this,ou);
    	this.wrap[ou]("click", this.onClick,  this);
        this.el[ou]("focus",this.onFocus,this);
        this.el[ou]("blur",this.onBlur,this);
        this.wrap[ou]("mousedown", this.onMouseDown,  this);
    },
    initEvents : function(){
    	$S.XButton.superclass.initEvents.call(this);
    	this.addEvents(
    	/**
         * @event click
         * 鼠标点击事件.
         * @param {Spark.XButton} button 按钮对象.
         * @param {EventObject} e 键盘事件对象.
         */
    	'click');
    },    
    destroy : function(){
		$S.XButton.superclass.destroy.call(this);
    	delete this.el;
    },
//    destroy : function(){
//    	$S.XButton.superclass.destroy.call(this);
//    	this.el.un("click", this.onClick,  this);
//    	delete this.el;
//    },
	/**
	 * 获取焦点
	 */
	focus: function(){
		if(this.disabled)return;
		this.el.dom.focus();
	},
	/**
	 * 失去焦点
	 */	
	blur : function(){
    	if(this.disabled) return;
    	this.el.dom.blur();
    },
    /**
     * 设置不可用状态
     */
    disable: function(){
    	this.disabled = true;
        this.wrap.removeClass([this.overCss,this.pressCss])
    	this.wrap.addClass(this.disableCss);
    	this.el.dom.disabled = true;
    },
    /**
     * 设置可用状态
     */
    enable: function(){
    	this.disabled = false;
    	this.wrap.removeClass(this.disableCss);
    	this.el.dom.disabled = false;
    },
    onMouseDown: function(e){
    	if(!this.disabled){
        	this.wrap.addClass(this.pressCss);
        	Ext.get(document.documentElement).on("mouseup", this.onMouseUp, this);
    	}
    },
    onMouseUp: function(e){
    	if(!this.disabled){
        	Ext.get(document.documentElement).un("mouseup", this.onMouseUp, this);
        	this.wrap.removeClass(this.pressCss);
    	}
    },
    onClick: function(e){
    	if(!this.disabled){
        	e.stopEvent();
        	this.fireEvent("click", this, e);
    	}
    },
    onFocus : function(e){
        this.isFocused = true;
        this.onMouseOver(e);
    },
    onBlur : function(e){
        this.isFocused = false;
        this.onMouseOut(e)
    },
    onMouseOver: function(e){
    	if(!this.disabled) {
            this.wrap.addClass(this.overCss);
    	}
    },
    onMouseOut: function(e){
    	if(!this.disabled)
    	this.wrap.removeClass(this.overCss);
    }
});
$S.XButton.getTemplate = function(id,text,width){
    width = width||70;
    return '<TABLE class="s_btn " id="'+id+'" cellSpacing="0"><TBODY><TR><TD class="s_btn-tl"><I></I></TD><TD class="s_btn-tc"></TD><TD class="s_btn-tr"><I></I></TD></TR><TR><TD class="s_btn-ml"><I></I></TD><TD class="s_btn-mc"><button hideFocus style="height: 20px;width:'+(width-6)+'px" stype="btn"><span style="line-height:20px;height:20px;">'+text+'</span></BUTTON></TD><TD class="s_btn-mr"><I></I></TD></TR><TR><TD class="s_btn-bl"><I></I></TD><TD class="s_btn-bc"></TD><TD class="s_btn-br"><I></I></TD></TR></TBODY></TABLE><script>new Spark.XButton({"id":"'+id+'"});</script>';
}
$S.SwitchButton = Ext.extend($S.ViewComponent,{
    curCls:'cur',
    initComponent : function(config){
        $S.SwitchButton.superclass.initComponent.call(this, config);
    },
    processListener: function(ou){
        $S.SwitchButton.superclass.processListener.call(this,ou);
        this.wrap[ou]('click',this.onClick, this);
    },
    initEvents:function(){
        $S.Tab.superclass.initEvents.call(this);   
        this.addEvents(
        /**
         * @event select
         * 选择事件.
         * @param {Aurora.Tab} tab Tab对象.
         * @param {String} newValue value1.
         * @param {String} oldValue value2.
         */
        'select'
        );
    },
    onClick : function(e){
        var li = Ext.fly(e.target).findParent('li');
        if(li)
        this.selectButton(Ext.fly(li).getAttributeNS("","value"));
    },
    selectButton:function(value){
        if(this.activeValue == value) return;
        var oli = this.wrap.child('li[value='+this.activeValue+']'),li = this.wrap.child('li[value='+value+']'),ov;
        if(li) {
            if(oli) {
                oli.removeClass(this.curCls);
                ov = Ext.fly(oli).getAttributeNS("","value");
            }
            li.addClass(this.curCls);
            this.activeValue = value;
            this.fireEvent('select', this, value,ov)
        }
    }
})
/**
 * @class Spark.MenuButton
 * @extends Spark.Button
 * <p>菜单按钮组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.MenuButton = Ext.extend($S.Button,{
    downCss:'s-btn-down',
    align:"r",
    initComponent : function(config){
        $S.MenuButton.superclass.initComponent.call(this,config);
        this.menuId = config.id+'_menu';
        this.align = config.align||this.align;
    },
    initEvents : function(){
        $S.MenuButton.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event down
         * down事件.
         * @param {Spark.MenuButton} button 按钮对象.
         */
        'down',
        /**
         * @event up
         * up事件.
         * @param {Spark.MenuButton} button 按钮对象.
         */
        'up');
    },
    processListener: function(ou){
        $S.MenuButton.superclass.processListener.call(this, ou);
        var menu = $(this.menuId);
        if(menu){
            menu[ou]("click", this.onMenuClick,  this);
        }
    },
    onMenuClick: function(){
        this.up();
    },
    onMouseDown: function(e){
        e.preventDefault();
        this.focus();
        if(!this.disabled){
            this.wrap.hasClass(this.downCss) ? this.up() : this.down();
        }
    },
    onDocumentMouseDown: function(e){
        if(this.wrap.contains(e.target) || ($(this.menuId) && $(this.menuId).isEventFromComponent(e.target))) return;
        this.up();
    },
    showMenu: function(){
        var menu = $(this.menuId);
        if(menu){
            menu.wrap.setDisplayed('block');
            var vs = (Ext.isIE6 || Ext.isIE7) ? 2 : 0, xy = this.wrap.getXY(),
            sl = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollLeft,
            st = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollTop,
            _x = xy[0] - sl,_y = xy[1] - st,
            W=menu.wrap.getWidth(),H=menu.wrap.getHeight(),
            PH=this.wrap.getHeight(),PW=this.wrap.getWidth(),
            BH=$S.getViewportHeight()-3,BW=$S.getViewportWidth()-3,
            x = this.align == 'r' ? ((_x+W)>BW ? (_x+PW-W):_x) : ((_x+PW-W)>0 ? (_x+PW-W) : _x),
            y = (_y+PH+H)>BH ? (_y-H) : (_y+PH);
            menu.show(x+sl-vs,y+st-vs);
        }
    },
    hideMenu: function(){
        var menu = $(this.menuId);
        if(menu){            
            menu.hide();
        }
    },
    down: function(){
        this.wrap.addClass(this.downCss);
        Ext.get(document.documentElement).on("mousedown", this.onDocumentMouseDown, this);
        this.showMenu();
        this.fireEvent("down", this);
    },
    up:function(){
        this.wrap.removeClass(this.downCss);
        Ext.get(document.documentElement).un("mousedown", this.onDocumentMouseDown, this);
        this.hideMenu();
        this.fireEvent("up", this);
    }
})
/**
 * @class Spark.CheckBox
 * @extends Spark.Component
 * <p>可选组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.CheckBox = Ext.extend($S.ViewComponent,{
	checkedCss:'s-ckb-c',
    hoverCls:'s-ckb-hover',
//	uncheckedCss:'s-ckb-u',
	readonyCheckedCss:'s-ckb-ro-c',
	readonlyUncheckedCss:'s-ckb-ro-u',
	constructor: function(config){
		config.checked = config.checked || false;
		config.disabled = config.disabled || false;
		$S.CheckBox.superclass.constructor.call(this,config);
	},
	initComponent:function(config){
		this.checkedValue = 'Y';
		this.unCheckedValue = 'N';
		$S.CheckBox.superclass.initComponent.call(this, config);
		this.wrap=Ext.get(this.id);
		this.el=this.wrap.child('div[stype=checkbox]');
        this.initStatus();
	},
	processListener: function(ou){
    	this.wrap[ou]('click',this.onClick,this);
    },
	initEvents:function(){
		$S.CheckBox.superclass.initEvents.call(this);  	
		this.addEvents(
		/**
         * @event click
         * 鼠标点击事件.
         * @param {Spark.CheckBox} checkBox 可选组件.
         * @param {Boolean} checked 选择状态.
         */
		'click');    
	},
	destroy : function(){
    	$S.CheckBox.superclass.destroy.call(this);
    	delete this.el;
    },
	onClick: function(e){
		if(this.wrap.dom != e.target && !this.disabled){
			this.checked = this.checked ? false : true;				
			this.setValue(this.checked);
			this.fireEvent('click', this, this.checked);
		}
	},
    focus : function(){
        this.el.focus();
    },
    blur : function(){
        this.el.blur();     
    },
    onFocus : function(){
        var sf = this;
        sf.el.addClass(sf.focusCss);
        sf.fireEvent('focus',sf);
    },
    onBlur : function(){
        var sf = this;
        sf.el.removeClass(sf.focusCss);
        sf.fireEvent('blur',sf);
    },
	setValue:function(v, silent){
		if(typeof(v)==='boolean'){
			this.checked=v?true:false;			
		}else{
			this.checked = (''+v == ''+this.checkedValue)
//			this.checked = v === this.checkedvalue ? true : false;
		}
		this.initStatus();
		var value = this.checked==true ? this.checkedValue : this.unCheckedValue;		
		$S.CheckBox.superclass.setValue.call(this,value, silent);
	},
	getValue : function(){
    	var v= this.value;
		v=(v === null || v === undefined ? '' : v);
		return v;
    },
//	setDisabled:function(b){
//		if(typeof(b)==='boolean'){
//			this.disabled=b?true:false;	
//			this.initStatus();		
//		}		
//	},
    onMouseOver: function(e){
        $S.CheckBox.superclass.onMouseOver.call(this,e);
        if(!this.disabled)
            this.el.addClass(this.hoverCls);
       
    },
    onMouseOut: function(e){
        $S.CheckBox.superclass.onMouseOut.call(this,e);
        if(!this.disabled)
            this.el.removeClass(this.hoverCls);
    },
    doDisabled :function(disabled){
        if(disabled){
            this.el.addClass(this.disabledCss);
        }else{
            this.el.removeClass(this.disabledCss);
        }
    },
	initStatus:function(){
        $S.CheckBox.superclass.initStatus.call(this);
		this.el.removeClass(this.checkedCss);
        if(this.checked) this.el.addClass(this.checkedCss);	
	}			
});
/**
 * @class Spark.Radio
 * @extends Spark.Component
 * <p>单选框组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Radio = Ext.extend($S.ViewComponent, {
	ccs:'s_radio_c',
	ucs:'s_radio_u',
	rcc:'s_radio_ro_c',
	ruc:'s_radio_ro_u',
//	optionCss:'s_radio-option',
	imgCss:'s_radio',
	valueField:'value',
	constructor: function(config){
		config.checked = config.checked || false;
		config.disabled = config.disabled || false;
		$S.Radio.superclass.constructor.call(this,config);		
	},
	initComponent:function(config){
		$S.Radio.superclass.initComponent.call(this, config);
		this.wrap=Ext.get(this.id);	
		this.nodes = Ext.DomQuery.select('.s_radio_option',this.wrap.dom);
		this.initStatus();
//		this.select(this.selectIndex);
	},	
	processListener: function(ou){
    	this.wrap[ou]('click',this.onClick,this);
    	this.wrap[ou]("keydown", this.onKeyDown, this);
    },
    focus : function(){
    	this.wrap.focus();
    },
    onKeyDown:function(e){
        this.fireEvent('keydown', this, e);
        var keyCode = e.keyCode;
        if(keyCode == 13)  {
            var sf = this;
            setTimeout(function(){
                sf.fireEvent('enterdown', sf, e)
            },5);
        }else if(keyCode==40){
            var vi = this.getValueItem();
            var i = this.options.indexOf(vi);
            if(i+1 < this.options.length){
                var v = this.options[i+1][this.valueField];
                this.setValue(v)
            }
        }else if(keyCode==38){
            var vi = this.getValueItem();
            var i = this.options.indexOf(vi);
            if(i-1 >=0){
                var v = this.options[i-1][this.valueField];
                this.setValue(v)
            }
        }
    },
	initEvents:function(){
		$S.Radio.superclass.initEvents.call(this); 	
		this.addEvents('click','keydown','enterdown');    
	},
	setValue:function(value,silent){
		if(value=='')return;
		$S.Radio.superclass.setValue.call(this,value, silent);
		this.initStatus();
//		this.focus();//why focus?
	},
	getItem: function(){
		var item = this.getValueItem();
		if(item!=null){
            item = new $S.Record(item);
		}
		return item;
	},
	getValueItem: function(){
	   var v = this.getValue();
	   var l = this.options.length;
	   var r = null;
	   for(var i=0;i<l;i++){
	       var o = this.options[i];
	       if(o[this.valueField]==v){
	           r = o;
	           break;
	       }
	   }	   
	   return r;
	},
	select : function(i){
		var v = this.getItemValue(i);
		if(v){
			this.setValue(v);
		}
	},
	getValue : function(){
    	var v = this.value;
		v=(v === null || v === undefined ? '' : v);
		return v;
    },
//	setDisabled:function(b){
//		if(typeof(b)==='boolean'){
//			this.disabled=b?true:false;	
//			this.initStatus();		
//		}
//	},
	initStatus:function(){
		var l=this.nodes.length;
		for (var i = 0; i < l; i++) {
			var node=Ext.fly(this.nodes[i]).child('.'+this.imgCss);		
			node.removeClass(this.ccs);
			node.removeClass(this.ucs);
			node.removeClass(this.rcc);
			node.removeClass(this.ruc);
			var value = Ext.fly(this.nodes[i]).getAttributeNS("","itemvalue");
			if((i==0 && !this.value) || value === this.value){
				this.disabled?node.addClass(this.rcc):node.addClass(this.ccs);				
			}else{
				this.disabled?node.addClass(this.ruc):node.addClass(this.ucs);		
			}
		}
	},
	getItemValue:function(i){
	   var node = Ext.fly(this.nodes[i]);
	   if(!node)return null;
	   var v = node.getAttributeNS("","itemvalue");
	   return v;
	},
	onClick:function(e) {
		if(!this.disabled){
			var l=this.nodes.length;
			for (var i = 0; i < l; i++) {
				var node = Ext.fly(this.nodes[i]);
				if(node.contains(e.target)){
					var v = node.getAttributeNS("","itemvalue");
					this.setValue(v);
					this.fireEvent('click',this,v);
					break;
				}
			}
			
		}		
	}	
});
/**
 * @class Spark.TextField
 * @extends Spark.Field
 * <p>文本输入组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.TextField = Ext.extend($S.Field,{
    initComponent : function(config){
        $S.TextField.superclass.initComponent.call(this, config);   
        if(this.inputCase){
            //TODO:java中增加style，不在脚本中处理,效率会提高一点点
            this.el.setStyle('text-transform',this.inputCase+'case');
        }
    },    
    //TODO:放在Field中比较合适
    isCapsLock: function(e){
        var keyCode  =  e.getKey();
        var isShift  =  e.shiftKey;
        if (((keyCode >= 65&&keyCode<=90)&&!isShift)||((keyCode>=97&&keyCode<=122)&&isShift)){
            if(this.dcl!=true)
            $S.showWarningMessage('警告', '大写键已经开启!');
            this.dcl = true;
        }else{
            this.dcl = false;
        }
    }, 
    onKeyPress : function(e){
        $S.TextField.superclass.onKeyPress.call(this,e);
//      if(e.isSpecialKey()){
//            return;//TODO:做什么用的?
//        }
        if(this.detectCapsLock) this.isCapsLock(e);
    }
})
/**
 * @class Aurora.NumberField
 * @extends Aurora.TextField
 * <p>数字输入组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 * TODO: allowDecimals  allowNegative 这种属性设置到field上,以便行可以动态设置
 */
$S.NumberField = Ext.extend($S.TextField,{
	allowDecimals : true,
    allowNegative : true,
    allowFormat : false,
	baseChars : "0123456789",
    decimalSeparator : ".",
    decimalPrecision : 2,
	constructor: function(config) {
        $S.NumberField.superclass.constructor.call(this, config);
    },
    initComponent : function(config){
        $S.NumberField.superclass.initComponent.call(this, config); 
        this.allowed = this.baseChars+'';
        if(this.allowDecimals){
            this.allowed += this.decimalSeparator;
        }
        if(this.allowNegative){
            this.allowed += "-";
        }
    },
//    initValue : function(v){
//    	this.setRawValue(this.formatValue(v))
////    	if(this.allowFormat){
////            this.setRawValue($S.formatNumber(v));
////    	}
//    },
    initEvents : function(){
    	$S.NumberField.superclass.initEvents.call(this);    	
    },
    onKeyPress : function(e){
        var k = e.getKey();
        if((Ext.isGecko || Ext.isOpera) && (e.isSpecialKey() || k == 8 || k == 46)){//BACKSPACE or DELETE
            return;
        }
        var c = e.getCharCode();
        if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
            e.stopEvent();
            return;
        }
        $S.NumberField.superclass.onKeyPress.call(this, e); 
    },
    formatValue : function(rv){
        var sf = this,rv = sf.fixPrecision(sf.parseValue(rv));
        if(sf.allowFormat)rv = $S.formatNumber(rv);
        return $S.NumberField.superclass.formatValue.call(sf,rv);
    },
    processMaxLength : function(rv){
        var s=rv.split('.'),isNegative=false;
        if(s[0].search(/-/)!=-1)isNegative=true;
        return (isNegative?'-':'')+$S.NumberField.superclass.processMaxLength.call(this, s[0].replace(/[-,]/g,''))+(s[1]?'.'+s[1]:''); 
    },
    processValue : function(v){
    	return this.parseValue(v);
    },
    onFocus : function(e) {
    	if(this.allowFormat&&!this.disabled) {
            this.setRawValue($S.removeNumberFormat(this.getRawValue()));
        }
    	$S.NumberField.superclass.onFocus.call(this,e);
    },
    parseValue : function(v){
        var sf = this;
        v = String(v);
        if(v.indexOf(",")!=-1)v=v.replace(/,/g,"");
    	if(!sf.allowNegative)v = v.replace('-','');
    	if(!sf.allowDecimals)v = v.indexOf(".")==-1?v:v.substring(0,v.indexOf("."));
        v = parseFloat(sf.fixPrecision(v.replace(this.decimalSeparator, ".")));
        return isNaN(v) ? '' : v;
    },
    fixPrecision : function(v){
        var nan = isNaN(v);
        if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !v){
           return nan ? '' : v;
        }
        return parseFloat(v).toFixed(this.decimalPrecision);
    }
})
/**
 * @class Spark.TriggerField
 * @extends Spark.TextField
 * <p>触发类组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.TriggerField = Ext.extend($S.TextField,{
    isExpanded : false,
	constructor: function(config) {
        $S.TriggerField.superclass.constructor.call(this, config);
    },
    initComponent : function(config){
        $S.TriggerField.superclass.initComponent.call(this, config);
        this.trigger = this.wrap.child('div[stype=trigger]');
        this.initPopup();
    },
    initPopup: function(){
    	if(this.initpopuped == true) return;
    	this.popup = this.wrap.child('div[stype=popup]');
        if(this.popup){
        	Ext.getBody().insertFirst(this.popup);
        	this.initpopuped = true;
        }
    },
    processListener: function(ou){
        $S.TriggerField.superclass.processListener.call(this, ou);
        this.trigger[ou]('click',this.onTriggerClick, this, {preventDefault:true});
        this.el[ou]("click",   this.onInputClick, this)
    },
    setHeight: function(h){
        $S.TriggerField.superclass.setHeight.call(this, h);
        this.trigger.setStyle("height",(h-2)+"px");
    },
    setWidth: function(w){
		this.wrap.setStyle("width",(w-2)+"px");
        this.el.setStyle("width",(w-28)+"px");
	},    
    /*onBlur : function(e){
//        if(this.isEventFromComponent(e.target)) return;
//    	if(!this.hasExpanded()){
	    	this.isFocused = false;
	        this.wrap.removeClass(this.focusCss);
	        this.fireEvent("blur", this);
//    	}
    },*/    
    isEventFromComponent:function(el){
    	return $S.TriggerField.superclass.isEventFromComponent.call(this,el)|| (this.popup && (this.popup.dom == el || this.popup.contains(el)));
    },
	destroy : function(){
        $S.TriggerField.superclass.destroy.call(this);
        this.collapse();
        if(this.popup)
    	this.popup.remove();
	},
    triggerBlur : function(e,t){
    	if(!this.isEventFromComponent(t)){    
    		this.collapse();
        }
    },
    setVisible : function(v){
    	$S.TriggerField.superclass.setVisible.call(this,v);
    	if(v == false){
    		this.collapse();
    	}
    },
    /**
     * 折叠弹出面板
     */
    collapse : function(){
        if(!this.isExpanded||!this.popup) return;
    	Ext.get(document.documentElement).un("mousedown", this.triggerBlur, this);
    	this.popup.moveTo(-10000,-10000);
        this.popup.dom.style.opacity=0;
    	//this.shadow.moveTo(-1000,-1000);
        this.isExpanded = false;
    },
    /**
     * 展开弹出面板
     */
    expand : function(){
        if(this.disabled || this.isExpanded||!this.popup) return;
        this.doExpand();
    },
    doExpand : function(){
        //TODO:对于某些行上的cb，如果是二级关联的情况下，会expand多次，导致多次绑定事件
        Ext.get(document.documentElement).un("mousedown", this.triggerBlur, this)
                                         .on("mousedown", this.triggerBlur, this);
        this.syncPopup();
        this.isExpanded = true;
    },
    syncPopup : function(){
        var sl = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollLeft,
            st = document[Ext.isStrict&&!Ext.isWebKit?'documentElement':'body'].scrollTop,
            xy = this.wrap.getXY(),_x = xy[0] - sl,_y = xy[1] - st,
            W=this.popup.getWidth(),H=this.popup.getHeight(),
            PH=this.wrap.getHeight(),PW=this.wrap.getWidth(),
            BH=$S.getViewportHeight()-3,BW=$S.getViewportWidth()-3,
            x=((_x+W)>BW?((BW-W)<0?_x:(BW-W)):_x)+sl,
            y=((_y+PH+H)>BH?((_y-H)<0?(_y+PH):(_y-H)):(_y+PH))+st;
        this.popup.moveTo(x,y);
        this.popup.dom.style.opacity=1;
    },
    onKeyDown: function(e){
        switch(e.keyCode){
            case 9://TAB
            case 13://ENTER
            case 27:
                if(this.isExpanded){
                    this.collapse();
                    this.focus();
                    e.stopEvent();
                }
                break;//ESC
            case 40:
                this.expand();//DOWN
        }
        $S.TriggerField.superclass.onKeyDown.call(this,e);
    },
    onTriggerClick : function(e){
    	if(!this.isExpanded){
    		this.expand();
            this.el.focus();
    	}else{
    		this.collapse();
    	}
    },
    onInputClick : function(e){
        this.onTriggerClick(e);
    }
});
/**
 * @class Spark.ComboBox
 * @extends Spark.TriggerField
 * <p>Combo组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.ComboBox = Ext.extend($S.TriggerField, {	
	maxHeight : 300,
	blankOption : true,
	rendered : false,
	selectedClass : 's_cb_sd',
    overClass: 's_cb_ov',
    initConfig : function(config) {
		$S.ComboBox.superclass.initConfig.call(this, config);
		if (config.options) {
			this.setOptions(config.options);
		} else {
			this.clearOptions();
		}
	},
    initStatus : function(){
        $S.ComboBox.superclass.initStatus.call(this);
        if (this.editable === false && this.disabled == false)
            this.el.dom.readOnly = true;
    },
    processListener: function(ou){
        $S.ComboBox.superclass.processListener.call(this, ou);
        if(this.popup){
            this.popup[ou]('click', this.onViewClick,this);
            this.popup[ou]('mousemove',this.onViewMove,this);
        }
    },
	initEvents:function(){
		$S.ComboBox.superclass.initEvents.call(this);
		this.addEvents(
		/**
         * @event select
         * 选择事件.
         * @param {Spark.Combobox} combo combo对象.
         * @param {Object} value valueField的值.
         * @param {Object} display displayField的值.
         * @param {Spark.Record} record 选中的Record对象
         */
		'select');
	},
	initQuery: function(){//事件定义中调用
		this.doQuery(this.getText());
	},
    initList: function(){
        this.refresh();
        if(this.optionDataSet.loading == true){//TODO:需要测试
            this.view.update('<li>正在加载...</li>');
        }else{
            var datas = this.optionDataSet.getAll(),
                l=datas.length,
                sb = [];
            for(var i=0;i<l;i++){
                var r = datas[i],text = this.getRenderText(r);
                sb.push('<li code="'+r.get(this.valueField)+'">'+text+'</li>');
            }
            if(l!=0){
                this.view.update(sb.join(''));          
            }
        }
        this.correctViewSize();
    },    
    getRecordByDisplay: function(name){
    	if(!this.optionDataSet)return null;
        var record = null;
        Ext.each(this.optionDataSet.getAll(),function(r){
            if(this.getRenderText(r) == name){
                record = r;
                return false;
            }
        },this);
        return record;
    },
	doExpand : function(){
		if(!this.optionDataSet)return;
		if(this.rendered === false)this.initQuery();
		var index = this.getIndex(this.getValue());
		this.clearSelectItem();
        if (index!=-1) {	
            this.selectItem(index);
		}		
		$S.ComboBox.superclass.doExpand.call(this);
	},
	/**
	 * 收起下拉菜单.
	 */
	collapse:function(){
		$S.ComboBox.superclass.collapse.call(this);	
        if(this.optionDataSet)this.optionDataSet.clearFilter();
	},
	clearOptions : function(){
	   this.processDataSet('un');
	   this.optionDataSet = null;
	},
	setOptions : function(name){
		var ds = name
		if(typeof(name)==='string'){
			ds = $(name);
		}
		if(this.optionDataSet != ds){
			this.optionDataSet = ds;
			this.processDataSet('un');
			this.processDataSet('on');
			this.rendered = false;
			if(!Ext.isEmpty(this.value)) this.setValue(this.value, true)
		}
	},
	processDataSet: function(ou){
		if(this.optionDataSet){
            this.optionDataSet[ou]('load', this.onDataSetLoad, this);
            this.optionDataSet[ou]('add', this.onDataSetLoad, this);
            this.optionDataSet[ou]('update', this.onDataSetLoad, this);
            this.optionDataSet[ou]('remove', this.onDataSetLoad, this);
            this.optionDataSet[ou]('clear', this.onDataSetLoad, this);
            this.optionDataSet[ou]('reject', this.onDataSetLoad, this);
		}
	},	
	correctViewSize: function(){
		var widthArray = [],mw = this.wrap.getWidth();
		for(var i=0;i<this.view.dom.childNodes.length;i++){
			var li=this.view.dom.childNodes[i];
			var width=$S.TextMetrics.measure(li.innerHTML).width;
            
			mw = Math.max(mw,width)||mw;
		}
		var lh = Math.min(this.popup.child('ul').getHeight()+2,this.maxHeight); 
        //mw = mw + (( lh < this.maxHeight) ? 10 : 27);//scrollbar
		this.popup.setWidth(mw).setHeight(lh<20?20:lh);
//    	this.shadow.setWidth(mw).setHeight(lh<20?20:lh);
	},
	doQuery : function(q) {
        var ds = this.optionDataSet;
        if(!ds)return;
        if(Ext.isEmpty(q)){
            ds.clearFilter();
        }else {            
            var reg = new RegExp(q.replace(/[+?*.^$\[\](){}\\|]/g,function(v){
                    return '\\'+v;
                }),'i');
            ds.filter(function(r){
                return reg.test(this.getRenderText(r));
            },this);
        }
		this.draw();
	},
    draw:function(){
        if(!this.view){
            this.popup.update('<ul></ul>');
            this.view=this.popup.child('ul');
        }
        if(this.optionDataSet){
            this.initList();
            this.rendered = true;
        }       
    },  
	getRenderText : function(record){
        if(!record) return '';
        var rder = $S.getFunction(this.displayRenderer),
            text = '&#160;';
        if(rder){
            text = rder.call(window,record);
        }else{
            text = record.get(this.displayField);
        }
		return text;
	},
	refresh:function(){
		this.view.update('');
        this.selectIndex = null;
		this.currentIndex = null;
	},
    highlightItem:function(idx1,idx2,cls){
        if(Ext.isEmpty(idx1)) return;
        if(idx1 != idx2) {
            var cn = this.getNode(idx2),tn = this.getNode(idx1);
            if(tn){
                if(cn)Ext.fly(cn).removeClass(cls);
                Ext.fly(tn).addClass(cls);                
                return true;
            }
            return false;
        }
    },
    clearSelectItem: function(){
        var sn = this.getNode(this.selectIndex);
        if(sn)Ext.fly(sn).removeClass(this.selectedClass);
        var cn = this.getNode(this.currentIndex);
        if(cn)Ext.fly(cn).removeClass(this.overClass);
        this.selectIndex = null;
        this.currentIndex = null;
        this.popup.scrollTo('top',0);
    },
	selectItem:function(index){
        if(this.highlightItem(index,this.selectIndex,this.selectedClass)){
            this.selectIndex = index;
            this.doFocusRow(index);
        }
	},
    overItem : function(index,focus){
        if(this.highlightItem(index,this.currentIndex,this.overClass)){
            this.currentIndex = index;
            if(focus) this.doFocusRow(index);
        }
    },
    doFocusRow : function(row){
        var r = 22,
            ub = this.popup,
            stop = ub.getScroll().top,
            h = ub.getHeight(),
            sh = ub.dom.scrollWidth > ub.dom.clientWidth? 16 : 0;
        if(row*r<stop){
            ub.scrollTo('top',row*r-1)
        }else if((row+1)*r>(stop+h-sh)){//this.ub.dom.scrollHeight
            ub.scrollTo('top', (row+1)*r-h + sh);
        }
    },
	getNode:function(index){		
		return this.view.dom.childNodes[index];
	},	
	destroy : function(){
    	$S.ComboBox.superclass.destroy.call(this);
		delete this.view;
	},
	getText : function() {		
		return this.text;
	},
    processValue : function(rv){
        return  this.getRecordByDisplay(rv) ? rv : '';
    },
    setValue: function(v, silent){
        $S.ComboBox.superclass.setValue.call(this, v, silent);
        if(this.record){
            var field = this.record.getField(this.binder.name);
            if(field && !silent){
                var mapping = field.get('mapping'),raw = this.getRawValue(),record = this.getRecordByDisplay(raw);
                if(mapping){
                    for(var i=0;i<mapping.length;i++){
                        var map = mapping[i],vl = record ? record.get(map.from) : '';
                        if(map.from == this.displayField) continue;//上面已经setValue过,防止有displayRenderer的时候被重新覆盖掉
                        if(!Ext.isEmpty(vl,true)){
                            this.record.set(map.to,vl);
                        }else{
                            delete this.record.data[map.to];
                        }
                        
                    }
                }
            }
        }
    },
	getIndex:function(v){
		var datas = this.optionDataSet.getAll(), l = datas.length;
		for (var i = 0; i < l; i++) {
			if (this.getRenderText(datas[i]) == v) {
				return i;
			}
		}
        return -1;
	},
	onViewClick:function(e,t){
		if(t.tagName=='LI'){
		    this.selectRow(Ext.fly(t).getAttributeNS("","code"));
            this.collapse();
            this.focus();
		}
	},
	onViewMove:function(e,t){
        if(t.tagName=='LI'){
            var ods = this.optionDataSet, r = ods.find(this.valueField,Ext.fly(t).getAttributeNS("","code"));
            if(r){
                this.overItem(ods.indexOf(r));
            }
        }    
	},
	selectRow:function(v){
		if(!v)return;
        var r = this.optionDataSet.find(this.valueField,v),t = this.getRenderText(r);
        this.fireEvent('select',this, v,t,r);
        this.setValue(t);
	},
//    processValue : function(rv){
//        var r = this.getRecordByDisplay(rv);
//        if (r != null) {
//            return r.get(this.valueField);
//        } else {
//            return '';
//        }
//    },
//    onBlur : function(e) {
//        $S.ComboBox.superclass.onBlur.call(this, e);
//        if (this.isFocused && !this.isExpanded) {// TODO:grid离开编辑器会触发两次?
//            var raw = this.getRawValue();
//            var record = this.getRecordByDisplay(raw);
//            if (record != null) {
//                this.setValue(record.get(this.displayField));
//            } else {
//                this.setValue('');
//            }
//        }
//    },    
	onDataSetLoad: function(){
		this.rendered = false;
		if(this.isExpanded) {
            this.expand();
		}
	},  
    onKeyUp : function(e){
        if(this.disabled|| this.editable === false)return;
        var c = e.keyCode;
        if(!e.isSpecialKey()){
            this.doQuery(this.getRawValue());
            if(!this.isExpanded) {
                $S.ComboBox.superclass.expand.call(this);
            } else {
                this.syncPopup();
            }
            this.rendered = false;
        }
        $S.ComboBox.superclass.onKeyUp.call(this, e);
    },
    onKeyDown: function(e){
        if(this.disabled)return;
        var current = Ext.isEmpty(this.currentIndex) ? -1 : this.currentIndex,
            keyCode = e.keyCode;
        if(this.isExpanded && (keyCode == 40||keyCode == 38)) {// 38-UP  40-DOWN
            if(keyCode == 38){
                current --;
                if(current>=0){
                    this.overItem(current,true)
                }            
            }
            if(keyCode == 40){
                current ++;
                if(current<this.view.dom.childNodes.length){
                    this.overItem(current,true)
                }
            }
        } else if(this.isExpanded && keyCode == 13){//13-ENTER
            if(current==-1){
                this.collapse();
                return;
            }else {
                this.selectItem(current);
                this.selectRow(Ext.fly(this.getNode(current)).getAttributeNS("","code"));
            }
        }
        $S.ComboBox.superclass.onKeyDown.call(this,e);
    }    
});
/**
 * @class Spark.DateField
 * @extends Spark.Component
 * <p>日期组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.DateField = Ext.extend($S.ViewComponent, {
	constructor: function(config) {
        $S.DateField.superclass.constructor.call(this,config);
    },
    initComponent : function(config){
        $S.DateField.superclass.initComponent.call(this, config);
        this.cellHeight = Math.round((this.height-36)/6);
        this.ct = this.ct||this.id;
        this.initTemplate();
        if(this.sbg) this.className += ' dbg'
        this.wrap = this.tpl.append(Ext.get(this.ct).dom,this,true);
        this.tbody = this.wrap.child("tbody");
        this.preBtn = this.wrap.child("div.s_df_pre");
        this.nextBtn = this.wrap.child("div.s_df_next");
        if(this.showPreBtn==false)this.preBtn.hide();
        if(this.showNextBtn==false)this.nextBtn.hide();
        this.textSpan = this.wrap.child("div.s_df_text");
        this.predraw(this.initDate || new Date());
    },
    initTemplate : function(){
        this.tpl = new Ext.Template(
            '<table class="{className}" style="width:{width}px;{style}" id="{id}" cellspacing="0">',
                '<thead>',
                    '<tr class="s_df_h">',
                        '<td colspan="7">',
                            '<div class="s_df_pre"></div>',
                            '<div class="s_df_next"></div>',
                            '<div class="s_df_text"></div>',
                        '</td>',
                    '</tr>',
                    '<tr class="s_df_w">',
                        '<td>日</td>',
                        '<td>一</td>',
                        '<td>二</td>',
                        '<td>三</td>',
                        '<td>四</td>',
                        '<td>五</td>',
                        '<td>六</td>',
                    '</tr>',
                '</thead>',
                '<tbody/>',
            '</table>');
    },
    processListener: function(ou){
    	$S.DateField.superclass.processListener.call(this,ou);
    	this.preBtn[ou]("click", this.preMonth, this);
        this.nextBtn[ou]("click", this.nextMonth, this);
    	this.wrap[ou]("click", this.onClick, this);
    	this.wrap[ou]('mousewheel',this.onMouseWheel,this);
    },
    initEvents : function(){
    	$S.DateField.superclass.initEvents.call(this);   	
    	this.addEvents(
    	/**
         * @event select
         * 选择事件.
         * @param {Spark.DateField} dateField 日期组件.
         * @param {Date} date 选择的日期.
         */
    	'select',
    	/**
         * @event unselect
         * 取消选择事件.
         * @param {Spark.DateField} dateField 日期组件.
         */
    	'unselect',
    	/**
         * @event render
         * 绘制事件.
         * @param {Spark.DateField} dateField 日期组件.
         */
    	'render',
    	/**
         * @event pre
         * 上一个月.
         * @param {Spark.DateField} dateField 日期组件.
         */
        'pre',
        /**
         * @event next
         * 下一个月.
         * @param {Spark.DateField} dateField 日期组件.
         */
        'next',
        /**
         * @event over
         * over事件.
         * @param {Spark.DateField} dateField 日期组件.
         * @param {Date} date 选择的日期.
         */
        'over');
    },
    destroy : function(){
    	delete this.overTd;
    	$S.DateField.superclass.destroy.call(this);
	},
	onMouseWheel : function(e){
        var delta = e.getWheelDelta();
        if(delta > 0){
            this.preMonth.defer(1,this);
        } else if(delta < 0){
            this.nextMonth.defer(1,this);
        }
        e.stopEvent();
    },
    onMouseOver: function(e){
        $S.DateField.superclass.onMouseOver.call(this,e);
    	var target = Ext.fly(e.target).findParent('td.s_day');
    	if(target && Ext.fly(target).getAttributeNS('','_date') != '0'){
    		this.focusDate(target);
    	}
    },
    onClick : function(e){
        var target = Ext.fly(e.target).findParent('td.s_day');
        this.selectTarget(target);
    },
    selectTarget: function(t){
        if(t) {
            var _d = Ext.fly(t).getAttributeNS('','_date');
        	if(_d != '0') this.select(new Date(parseInt(_d)));
    	}
    },
    focusDate : function(t){
        this.blurDate();
        this.overTd = t;
        Ext.fly(this.overTd).addClass('over');
        var d = new Date(parseInt(Ext.fly(t).getAttributeNS('','_date')));
        this.fireEvent('over', this, d);
    },
    blurDate : function(){
        if(this.overTd) {
            Ext.fly(this.overTd).removeClass('over');
            this.overTd = null;
        }
    },
    select : function(d){
    	if(!d)return;
    	if(this.sd) this.unselect(this.sd);
        if(d){
        	var cell = this.cache['_'+d.getTime()];
            if(cell && !Ext.fly(cell).hasClass('select'))Ext.fly(cell).addClass('select');
        }
        this.sd = d;
        this.fireEvent('select', this, this.sd);
    },
    unselect : function(d){
    	if(!d)return;
    	var cell = this.cache['_'+d.getTime()];
        if(cell){
            Ext.fly(cell).removeClass('select');
        }
        this.sd = null;
        this.fireEvent('unselect', this);
    },
	onSelectDay: function(o){
		if(!Ext.fly(o).hasClass('select'))Ext.fly(o).addClass('select');
	},
	onToday: function(o){
		Ext.fly(o).addClass("today");
	},
	/**
     * 上一月
     */
    preMonth: function(v,fire) {
        this.predraw(new Date(this.year, this.month - 2 - (v ? v - 1 : 0), 1));
        if(fire!=false)this.fireEvent('pre', this, v);
    },
    /**
     * 下一月
     */
    nextMonth: function(v,fire) {
        this.predraw(new Date(this.year, this.month+(v ? v - 1 : 0), 1));
        if(fire!=false)this.fireEvent('next', this, v);
    },
  	/**
  	 * 根据日期画日历
  	 * @param {Date} date 当前日期
  	 */
  	predraw: function(date) {
  		if(date=='' || !date.getFullYear) date = new Date();
  		this.date = date;
        this.year = date.getFullYear(); 
        this.month = date.getMonth() + 1;
        this.textSpan.update(date.format(this.format||"yyyy-mm"))
        this.draw();
        if(this.sbg) {
            this.wrap.removeClass(['dbg1','dbg2','dbg3','dbg4','dbg5','dbg6','dbg7','dbg8','dbg9','dbg10','dbg11','dbg12']);
            this.wrap.addClass('dbg'+this.month);
        }
        this.overTd = null;
        this.fireEvent('render', this);
  	},
	draw: function() {
		if(this.drawing) return;
		this.drawing = true;
		//用来保存日期列表
		this.cache = {};
        var arr = [];
        //用当月第一天在一周中的日期值作为当月离第一天的天数,用上个月的最后天数补齐
        for(var i = 1, firstDay = new Date(this.year, this.month - 1, 1).getDay(),lastDay = new Date(this.year, this.month - 1, 0).getDate(); i <= firstDay; i++){
            arr.push([n=lastDay-firstDay+i,new Date(this.year, this.month - 2, n),"s_day s_day_pre"]); 
        }
        //用当月最后一天在一个月中的日期值作为当月的天数
        for(var i = 1, monthDay = new Date(this.year, this.month, 0).getDate(); i <= monthDay; i++){ 
            arr.push([i,new Date(this.year, this.month - 1, i),"s_day"]); 
        }
        //用下个月的前几天补齐
        var l = 42 - arr.length;
        for(var i=1;i<=l;i++){
            arr.push([i,new Date(this.year, this.month, i),"s_day s_day_next"]); 
        }
        
        while(this.tbody.dom.firstChild){ 
          Ext.fly(this.tbody.dom.firstChild).remove();
        }
        
        var k=0;
        while(arr.length){
        	var row = document.createElement("TR");
            Ext.fly(row).set({'_r':k});
            row.style.height=this.cellHeight+"px";
            k++;
            for(var i = 1; i <= 7; i++){
                if(arr.length){
                    var d = arr.shift();
                    var cell = document.createElement("TD");
                    if(d &&d[1]){
                      var key = (''+d[1].getTime())
                      this.cache['_'+key]=cell;
                        cell.className = d[2];
                        cell.innerHTML = d[0];
                        this.renderCell(cell,d[1]);
                        var cf = Ext.fly(cell);
                        cf.set({'_c':i-1});
                        if(cell.disabled){
                            cf.set({'_date':'0'});
                            cf.addClass("s_day_disabled");
                        }else {
                            cf.set({'_date':key,'title':d[1].format('yyyy-mm-dd')});
                        }
                        if(d[2] == 's_day') {
                            if(new Date().isSame(d[1])) cell.className = cell.className + " today";
                            if(this.sd && d[1].isSame(this.sd)) this.onSelectDay(cell);
                        }
                    }
                    row.appendChild(cell);
                }
            }
            this.tbody.dom.appendChild(row);
        }
        this.drawing = false;
	},
	renderCell:function(cell,date){
		if(this.renderer)$S.getFunction(this.renderer).call(this,cell,date);
	}	
//	,isSame: function(d1, d2) {
//		if(!d2.getFullYear||!d1.getFullYear)return false;
//		return (d1.getFullYear() == d2.getFullYear() && d1.getMonth() == d2.getMonth() && d1.getDate() == d2.getDate());
//	}
});
/**
 * @class Spark.DatePicker
 * @extends Spark.TriggerField
 * <p>DatePicker组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.DatePicker = Ext.extend($S.TriggerField,{
	constructor: function(config) {
        this.dateFields = [];
        $S.DatePicker.superclass.constructor.call(this,config);
    },
    initComponent : function(config){ 
        $S.DatePicker.superclass.initComponent.call(this,config);
        this.initFormat();
        this.initFooter();
    },
    initFooter : function(){},//TODO: today button?
    initFormat : function(){
        this.format=this.format||$S.defaultDateFormat;
    },
    initDateField:function(){
    	if(this.inited) return;
    	this.inited = true;
    	this.size=this.size||1;
        this.popup.setStyle('width',(180*this.size + 4 + (this.size-1)*2)+"px");
    	var t =  this.getValue() || new Date();
    	var y = t.getFullYear();
    	var m = t.getMonth();
    	if(this.dateFields.length==0){
    		for(var i=0;i<this.size;i++){
    			var p = i==0,n = i==(this.size-1);
                var cfg = {
                    id:this.id+'_df'+i,
                    width:180,
                    showPreBtn:p,
                    showNextBtn:n,
                    height:158,
                    sbg:true,
                    className:"s_df",
                    initDate:new Date(y, m, 1),
                    listeners:{
                    	'select':this.onSelect.createDelegate(this),
                    	'pre':this.onPre.createDelegate(this),
                    	'next':this.onNext.createDelegate(this),
                    	'over':this.onOver.createDelegate(this),
                        "mouseover":this.mouseOver.createDelegate(this)
                    }
                }
    			this.dateFields.push(new $S.DateField(cfg));
    			m++;
    		}
    	}
        this.focusField = this.dateFields[0];
//    	this.shadow.setWidth(this.popup.getWidth()).setHeight(this.popup.getHeight());
    },
    initEvents : function(){
    	$S.DatePicker.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event select
         * 选择事件.
         * @param {Spark.DatePicker} datePicker 日期选择组件.
         * @param {Date} date 选择的日期.
         */
        'select');
    },
    mouseOver: function(c,e){
        this.focusField = c
    },
    onKeyDown: function(e){
        if(this.disabled) return;
        if(this.isExpanded){
            var kc = e.keyCode;
            switch(kc){
                case 37:this.goLeft(e);break;
                case 38:this.goUp(e);break;
                case 39:this.goRight(e);break;
                case 40:this.goDown(e);break;
                case 33:this.focusField.preMonth();break;
                case 34:this.focusField.nextMonth();break;
                case 13:this.focusField.selectTarget(this.focusField.overTd);
            }
        }
        $S.DatePicker.superclass.onKeyDown.call(this,e);
    },
    getFieldOverTd : function(){
        var ff = this.focusField;
        if(!ff.overTd){
            if(ff.sd)ff.overTd = ff.cache['_'+ff.sd.getTime()]
            if(!ff.overTd)ff.focusDate(ff.tbody.first().child('td.s_day').dom);        
        }
        return ff.overTd;
    },
    goLeft : function(e){
        var field = this.focusField,fs = this.dateFields;
        var td = this.getFieldOverTd(),prev = Ext.fly(td).prev('.s_day'),parent = Ext.fly(td).parent(),r = parent.getAttributeNS('','_r');
        if(prev)field.focusDate(prev);
        else{
            var f = fs[fs.indexOf(field)-1];
            if(f)this.focusField = f;
            else{
                field.preMonth();
                this.focusField = fs[fs.length-1];
            }
            var l = this.focusField.tbody.child('tr[_r='+r+']').select('td.s_day');
            this.focusField.focusDate(l.item(l.getCount()-1).dom);
        }
        e.stopEvent();
    },
    goRight : function(e){
        var field = this.focusField,fs = this.dateFields;
        var td = this.getFieldOverTd(),next = Ext.fly(td).next('.s_day'),parent = Ext.fly(td).parent(),r = parent.getAttributeNS('','_r');;
        if(next) field.focusDate(next);
        else{
            var f = fs[fs.indexOf(field)+1];
            if(f)this.focusField = f;
            else{
                field.nextMonth();
                this.focusField = fs[0];
            }
            this.focusField.focusDate(this.focusField.tbody.child('tr[_r='+r+']').child('td.s_day').dom);
        }
        e.stopEvent();
    },    
    goUp : function(e){
        var field = this.focusField,fs = this.dateFields,
            td = this.getFieldOverTd(),prev = Ext.fly(td).parent().prev(),
            index = Ext.fly(td).getAttributeNS('','_c'),t;
        if(prev)t = prev.child('td[_c='+index+']').dom;
        if(t)field.focusDate(t);
        else {
            field.preMonth(this.size);
            var l = this.focusField.tbody.select('td[_c='+index+']');
            this.focusField.focusDate(l.item(l.getCount()-1).dom);
        }
    },
    goDown : function(e){
        var field = this.focusField,fs = this.dateFields,
            td = this.getFieldOverTd(),next = Ext.fly(td).parent().next(),
            index = Ext.fly(td).getAttributeNS('','_c'),t;
        if(next)t = next.child('td[_c='+index+']').dom;
        if(t)field.focusDate(t);
        else {
            field.nextMonth(this.size);
            this.focusField.focusDate(this.focusField.tbody.child('td[_c='+index+']').dom);
        }
    },
    onKeyUp: function(e){
        if(this.disabled)return;
    	$S.DatePicker.superclass.onKeyUp.call(this,e);
        var c = e.keyCode;
        if(!e.isSpecialKey()){
        	try{
        		var td = this.getRawValue().parseDate(this.format);
                this.wrapDate(td);
                this.predraw(td)
        	}catch(e){}
        }
    },
    selectDate : function(date){
        this.collapse();
        this.processDate(date);
        this.setValue(date);
        this.fireEvent('select',this, date);
        this.focus();
    },
    onSelect : function(field, date){
    	Ext.each(this.dateFields, function(df){
            if(df != field) df.unselect(df.sd);
        })
        this.selectDate(date);
    },
    wrapDate : function(d){},
    processDate : function(d){},
    onPre : function(field,prev){
        Ext.each(this.dateFields, function(df){
            if(df != field) {
                df.sd = field.sd;
                df.preMonth(prev,false);
            }
        })
//        this.focusField = null;
    },
    onNext : function(field,next){
        Ext.each(this.dateFields, function(df){
            if(df != field) {
            	df.sd = field.sd;
                df.nextMonth(next, false);         
            }
        })
//        this.focusField = null;
    },
    onOver : function(field){
        Ext.each(this.dateFields, function(df){
            if(df != field && df.overTd) Ext.fly(df.overTd).removeClass('over');        
        })
    },
    processValue : function(rv){
        if(Ext.isEmpty(rv)){
            return null;
        }else {
            try{
                var pd = rv.parseDate(this.format);
                this.wrapDate(pd)
                return pd;
            }catch(e){
                $S.showErrorMessage("错误","日期格式不正确!");
                return null;
            }
        }
    },
    formatValue : function(date){
    	if(date instanceof Date)return date.format(this.format);
    	return date;
    },
    doExpand : function(){
    	this.initDateField();
    	var v = this.getValue()
        if(!Ext.isEmpty(v)) {
        	this.predraw(v)
        }
    	$S.DatePicker.superclass.doExpand.call(this);
    },
    collapse : function(){
        $S.DatePicker.superclass.collapse.call(this);
        if(this.focusField)this.focusField.blurDate();
//        this.focusField = null;
    },
    predraw : function(v){
        if(v){
            var y = v.getFullYear(), m = v.getMonth(),cf = this.dateFields[0];
            cf.sd = v;
            cf.predraw(v);
            Ext.each(this.dateFields, function(df){
                if(df != cf) {
                    df.sd = v; 
                    df.predraw(new Date(y, ++m, 1));
                }
            })
        }
    },
    destroy : function(){
    	$S.DatePicker.superclass.destroy.call(this);
	}
});
/**
 * @class Spark.DateTimePicker
 * @extends Spark.DatePicker
 * <p>DateTimePicker组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.DateTimePicker = Ext.extend($S.DatePicker,{
    constructor: function(config) {
        $S.DateTimePicker.superclass.constructor.call(this,config);
        this.size = 1;
    },
    initFormat : function(){
        this.format=this.format||$S.defaultDateTimeFormat;
    },
    initFooter : function(){
        this.hourSpan = this.popup.child("input[stype=field.hour]");
        this.minuteSpan = this.popup.child("input[stype=field.minute]");
        this.secondSpan = this.popup.child("input[stype=field.second]");
    },
    processListener : function(ou){
        $S.DateTimePicker.superclass.processListener.call(this,ou);
        this.hourSpan[ou]("focus", this.onDateFocus, this);
        this.hourSpan[ou]("blur", this.onTimeInputBlur, this);
        this.hourSpan[ou]("keydown", this.onDateKeyDown, this);
        this.hourSpan[ou]("keyup", this.onDateKeyUp, this);
        this.minuteSpan[ou]("focus", this.onDateFocus, this);
        this.minuteSpan[ou]("blur", this.onTimeInputBlur, this);
        this.minuteSpan[ou]("keydown", this.onDateKeyDown, this);
        this.minuteSpan[ou]("keyup", this.onDateKeyUp, this);
        this.secondSpan[ou]("focus", this.onDateFocus, this);
        this.secondSpan[ou]("blur", this.onTimeInputBlur, this);
        this.secondSpan[ou]("keydown", this.onDateKeyDown, this);
        this.secondSpan[ou]("keyup", this.onDateKeyUp, this);
    },
    onDateKeyDown : function(e) {
        var c = e.keyCode, el = e.target;
        if (c == 13) {
            el.blur();
        } else if (c == 27) {
            el.value = el.oldValue || "";
            el.blur();
        } else if (c != 8 && c!=9 && c!=37 && c!=39 && c != 46 && (c < 48 || c > 57 || e.shiftKey)) {
            e.stopEvent();
            return;
        }
    },
    onDateKeyUp : function(e){
        var c = e.keyCode, el = e.target;
        if (c != 8 && c!=9 && c!=37 && c!=39 && c != 46 && (c < 48 || c > 57 || e.shiftKey)) {
            e.stopEvent();
            return;
        } else if(this.value&&this.value instanceof Date){
            var date=new Date(this.value.getTime());
            this.processDate(date);
            this.setValue(date);
            this.fireEvent('select',this, date);
        }
    },
    onDateFocus : function(e) {
        Ext.fly(e.target.parentNode).addClass("s_dateField-input-focus");
        e.target.select();
    },
    onTimeInputBlur : function(e) {
        var el = e.target;
        Ext.fly(el.parentNode).removeClass("s_dateField-input-focus");
        if(!el.value.match(/^[0-9]*$/))el.value=el.oldValue||"";
        else this.predraw(new Date(this.dateFields[0].year,this.dateFields[0].month - 1, 1,this.hourSpan.dom.value,this.minuteSpan.dom.value,this.secondSpan.dom.value));
    },
    predraw : function(date){
        $S.DateTimePicker.superclass.predraw.call(this,date);
        this.hourSpan.dom.oldValue = this.hourSpan.dom.value = $S.dateFormat.pad(date.getHours());
        this.minuteSpan.dom.oldValue = this.minuteSpan.dom.value = $S.dateFormat.pad(date.getMinutes());
        this.secondSpan.dom.oldValue = this.secondSpan.dom.value = $S.dateFormat.pad(date.getSeconds());
    },
    processDate : function(d){
        if(d){
            d.setHours((el=this.hourSpan.dom).value.match(/^[0-9]*$/)?el.value:el.oldValue);
            d.setMinutes((el=this.minuteSpan.dom).value.match(/^[0-9]*$/)?el.value:el.oldValue);
            d.setSeconds((el=this.secondSpan.dom).value.match(/^[0-9]*$/)?el.value:el.oldValue);
        }
        wrapDate(d)
    },
    wrapDate : function(d){
        d.xtype = 'timestamp';
    }
});
$S.ToolBar = Ext.extend($S.ViewComponent,{
//	constructor: function(config) {
//        $S.ToolBar.superclass.constructor.call(this, config);        
//    },
//    initComponent : function(){
//    	$S.ToolBar.superclass.initComponent.call(this);    	
//    },
//    initEvents : function(){
//    	$S.ToolBar.superclass.initEvents.call(this); 
//    }
})

$S.GridNavBar = Ext.extend($S.ToolBar,{
    initComponent : function(config){
        $S.GridNavBar.superclass.initComponent.call(this, config);
        this.dataSet = $(this.bind);
        this.info = this.wrap.child('div.info');
    },
    processListener: function(ou){
        $S.GridNavBar.superclass.processListener.call(this,ou);
        this.dataSet[ou]('load', this.onDataSetLoad,this);
    },
    onDataSetLoad : function(){
        var html=[],ds=this.dataSet,currentPage=ds.currentPage,totalCount=ds.totalCount,pageSize = ds.psize;
        html.push('第 <b>'+ds.currentIndex+'</b>');
        html.push(' - ');
        if(totalCount>currentPage*pageSize) {
            html.push('<b>'+currentPage*pageSize+'</b>');
        }else {
            html.push('<b>'+totalCount+'</b>');
        }
        html.push(' 条,');
        html.push(' 共 ' + '<b>'+totalCount+'</b> 条');
        this.info.update(html.join(''))
    }
})

$S.NavBar = Ext.extend($S.ToolBar,{
//	constructor: function(config) {
//        $S.NavBar.superclass.constructor.call(this, config);        
//    },
    initComponent : function(config){
        $S.NavBar.superclass.initComponent.call(this, config);
    	this.dataSet = $(this.dataSet);
    	this.navInfo = this.wrap.child('div[stype=displayInfo]');//Ext.get(this.infoId);
        if(this.navType != "simple"){
        	this.pageInput = $(this.inputId);
        	this.currentPage = this.wrap.child('div[stype=currentPage]');
        	this.pageInfo = this.wrap.child('div[stype=pageInfo]');//Ext.get(this.pageId);
        	this.pageInput.setValue(1);
        	
        	if(this.comboBoxId){
        		var pageSize=[10,20,50,100];
        		if(pageSize.indexOf(this.dataSet.psize)==-1){
        			pageSize.unshift(this.dataSet.psize);
        			pageSize.sort(function(a,b){return a-b});
        		}
        		var datas=[];
        		while(Ext.isDefined(pageSize[0])){
        			var ps=pageSize.shift();
        			datas.push({'code':ps,'name':ps});
        		}
        		var dataset=new $S.DataSet({'datas':datas});
    	    	this.pageSizeInput = $(this.comboBoxId);
    	    	this.pageSizeInput.setOptions(dataset);
    	    	this.pageSizeInput.setValue(this.dataSet.psize);
        		this.pageSizeInfo = this.wrap.child('div[stype=pageSizeInfo]');
        		this.pageSizeInfo2 = this.wrap.child('div[stype=pageSizeInfo2]');
        		this.pageSizeInfo.update('每页显示');//TODO:多语言
        		this.pageSizeInfo2.update('条');
        	}
        	this.currentPage.update('页数');
        }
    },
    processListener: function(ou){
    	$S.NavBar.superclass.processListener.call(this,ou);
    	this.dataSet[ou]('load', this.onLoad,this);
        if(this.navType != "simple"){
        	this.pageInput[ou]('keydown', this.onInputKeyPress, this);
        	if(this.pageSizeInput){
        		this.pageSizeInput[ou]('select', this.onInputSelect, this);
        	}
        }
    },
    initEvents : function(){
    	$S.NavBar.superclass.initEvents.call(this);
    },
    onLoad : function(){
    	this.navInfo.update(this.creatNavInfo());
        if(this.navType != "simple"){
        	this.pageInput.setValue(this.dataSet.currentPage);
        	this.pageInfo.update('共' + this.dataSet.totalPage + '页');            
        }
    },
    creatNavInfo : function(){
        if(this.navType == "simple"){
            var html=[],ds=this.dataSet,currentPage=ds.currentPage,totalPage=ds.totalPage;
            var from = ((this.dataSet.currentPage-1)*this.dataSet.psize+1);
            var to = this.dataSet.currentPage*this.dataSet.psize;
            if(totalPage){
                //html.push('<span>共'+totalPage+'页</span>');
                //html.push(currentPage == 1 ? '<span>首页</span>' : this.createAnchor('首页',1));
                html.push('<span style="margin-right:10px;">共 '+this.dataSet.totalCount+' 条记录</span>');
                html.push(currentPage == 1 ? '<span >上一页</span>' : this.createAnchor('上一页',currentPage-1));
                for(var i = 1 ; i < 4 && i <= totalPage ; i++){
                    html.push(i == currentPage ? '<b>' + currentPage + '</b>' : this.createAnchor(i,i));
                }
                if(totalPage > this.maxPageCount){
                    if(currentPage > 5)this.createSplit(html);
                    for(var i = currentPage - 1;i < currentPage + 2 ;i++){
                        if(i > 3 && i < totalPage - 2){
                            html.push(i == currentPage ? '<b>' + currentPage + '</b>' : this.createAnchor(i,i));
                        }
                    }
                    if(currentPage < totalPage - 4)this.createSplit(html);
                }else if(totalPage > 6){
                    for(var i = 4; i < totalPage - 2 ;i++){
                        html.push(i == currentPage ? '<b>' + currentPage + '</b>' : this.createAnchor(i,i));
                    }
                }
                for(var i = totalPage - 2 ; i < totalPage + 1; i++){
                    if(i > 3){
                        html.push(i == currentPage ? '<b>' + currentPage + '</b>' : this.createAnchor(i,i));
                    }
                }
                html.push(currentPage == totalPage ? '<span>下一页</span>' : this.createAnchor('下一页',currentPage+1));
                //html.push(currentPage == totalPage ? '<span>尾页</span>' : this.createAnchor('尾页',totalPage));
            }
            return html.join('');
        }else{
        	var from = ((this.dataSet.currentPage-1)*this.dataSet.psize+1);
        	var to = this.dataSet.currentPage*this.dataSet.psize;
        	if(to>this.dataSet.totalCount) to = this.dataSet.totalCount;
        	if(to==0) from =0;
        	return '显示' + from + ' - ' + to + ' '+ '共' + this.dataSet.totalCount + ' 条';
        }
    },
    createAnchor : function(text,page){
        return '<a href="javascript:$(\''+this.dataSet.id+'\').page('+page+')">'+text+'</a>';
    },
    createSplit : function(html){
        html.push('<span>···</span>');
    },
    onInputKeyPress : function(input, e){
    	if(e.keyCode == 13){
    		var page = parseInt(input.getRawValue());
    		if(isNaN(page)){
    			input.setValue(this.dataSet.currentPage);
    		}else{
    			if(page>0 && page<=this.dataSet.totalPage) {
    				this.dataSet.page(page);
    			}else{
    				input.setValue(this.dataSet.currentPage);
    			}
    		}
    	}    	
    },
    onInputSelect : function(combo,value){
    	this.dataSet.psize=value;
    	this.dataSet.query();
    }
})
$S.WindowManager = function(){
    return {
        put : function(win){
        	if(!this.cache) this.cache = [];
        	this.cache.add(win)
        },
        getAll : function(){
        	return this.cache;
        },
        remove : function(win){
        	this.cache.remove(win);
        },
        get : function(id){
        	if(!this.cache) return null;
        	var win = null;
        	for(var i = 0;i<this.cache.length;i++){
    			if(this.cache[i].id == id) {
	        		win = this.cache[i];
    				break;      			
        		}
        	}
        	return win;
        },
        getZindex: function(){
        	var zindex = 40;
        	var all = this.getAll();
            if(all)
        	for(var i = 0;i<all.length;i++){
        		var win = all[i];
        		var zd = win.wrap.getStyle('z-index');
        		zd = (zd =='auto') ? 0 : Number(zd);
        		if(zd > zindex) zindex = zd;       		
        	}
        	return Number(zindex);
        }
    };
}();
/**
 * @class Spark.Window
 * @extends Spark.Component
 * <p>窗口组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Window = Ext.extend($S.ViewComponent,{
    focusCss:'w_fs',
	constructor: function(config) { 
		if($S.WindowManager.get(config.id))return;
        this.draggable = true;
        this.closeable = true;
        this.hasCloseBtn = true;
        this.autoCenter = true;
        this.modal = config.modal||true;
        this.className = config.className||'';
        this.cmps = {};
        $S.Window.superclass.constructor.call(this,config);
    },
    initComponent : function(config){
        var sf = this; 
        $S.WindowManager.put(sf);
        $S.Window.superclass.initComponent.call(this, config);
    	var windowTpl = new Ext.Template(sf.getTemplate(sf.title,sf.hasCloseBtn));
    	sf.width = 1*(sf.width||400);
        sf.wrap = windowTpl.insertFirst(document.body, {title:sf.title,className:sf.className,width:"width:"+1*(sf.width||400) + "px",height:sf.height ? ("height:" + 1*(sf.height)+ "px") : ""}, true);
    	sf.head = sf.wrap.child('div.s-dlg-head');
    	sf.body = sf.wrap.child('div.s-dlg-ct');
        sf.closeBtn = sf.wrap.child('div.s-dlg-close');
        if(this.autoCenter) sf.center();
        if(sf.url){
        	sf.showLoading();       
        	sf.load(sf.url,sf.params)
        }
    },
    processListener: function(ou){
    	$S.Window.superclass.processListener.call(this,ou);
    	if(this.closeBtn) {
    	   this.closeBtn[ou]("click", this.onCloseClick,  this);
    	}
    	if(!this.modal) this.wrap[ou]("click", this.toFront, this);
    	this.wrap[ou]("keydown", this.handleKeyDown,  this);
    	if(this.draggable)this.wrap[ou]('mousedown', this.onMouseDown,this);
    },
    initEvents : function(){
    	$S.Window.superclass.initEvents.call(this);
    	this.addEvents(
    	/**
         * @event close
         * 窗口关闭事件.
         * @param {Window} this 当前窗口.         * 
         */
    	'close',
    	/**
         * @event load
         * 窗口加载完毕.
         * @param {Window} this 当前窗口.
         */
    	'load');    	
    },
    handleKeyDown : function(e){
		var key = e.getKey();
		if(key == 9){ //tab
            
            var fk,lk,ck,cmp
            for(var k in this.cmps){
                cmp = this.cmps[k];
                if(!fk && cmp.focus){
                    fk=k;
                }
                lk=k;
                if(cmp.isFocused){
                    ck = k;
                }
            }
            if(e.shiftKey){
                var temp = lk;
                lk = fk;
                fk = temp;
            }
            
            if(ck==lk){
                e.stopEvent();
                if(cmp && cmp.blur)cmp.blur();
                this.cmps[fk].focus();
            }
        }else if(key == 27){//esc
			if(this.hasCloseBtn)this.close();
		}
    },
    center: function(){
    	var screenWidth = $S.getViewportWidth(),screenHeight = $S.getViewportHeight(),
            sl = document[Ext.isStrict?'documentElement':'body'].scrollLeft,
            st = document[Ext.isStrict?'documentElement':'body'].scrollTop,
            w = this.wrap.getWidth(),h = this.wrap.getHeight(),
    	    x = sl+Math.max((screenWidth - w)/2,0),
    	    y = st+Math.max((screenHeight - h)/2,0);
        this.wrap.moveTo(x,y);
        this.toFront();
    },
    getTemplate : function(hasHead,closeable) {
        return  [
            '<div class="s-dlg {className}" style="left:-10000px;top:-10000px;{width};{height}" tabIndex="-1" hidefocus>',
                hasHead ? '<div class="s-dlg-head s-dlg-draggable" unselectable="on">{title}</div>' : '',
                closeable ? '<div class="s-dlg-close" data-tooltip-type="tip" data-tooltip="关闭"></div>' : '',
                '<div class="s-dlg-ct"></div>',
            '</div>'
        ];
    },
    toFront : function(){ 
    	var myzindex = this.wrap.getStyle('z-index');
    	var zindex = $S.WindowManager.getZindex();
    	myzindex = (myzindex =='auto') ?  0 : Number(myzindex);
    	if(myzindex < zindex) {
	    	this.wrap.setStyle('z-index', zindex+5);
	    	if(this.modal) $S.Cover.cover(this.wrap,this.opacity);
    	}   	
    },
    onMouseDown : function(e){
    	var sf = this; 
    	sf.toFront();
        var v = Ext.fly(e.target).hasClass('s-dlg-draggable');
        if(v) {
            //if(sf.closeBtn && sf.closeBtn.contains(e.target))return;
            e.preventDefault();
        	var xy = sf.wrap.getXY();
        	sf.relativeX=xy[0]-e.getPageX();
    		sf.relativeY=xy[1]-e.getPageY();
    		sf.screenWidth = $S.getViewportWidth();
            sf.screenHeight = $S.getViewportHeight();
            if(!sf.proxy) sf.initProxy();
            sf.proxy.show();
            Ext.get(document.documentElement).on("mousemove", sf.onMouseMove, sf);
            Ext.get(document.documentElement).on("mouseup", sf.onMouseUp, sf);
        	sf.focus();
        }
        
    },
    onMouseUp : function(e){
    	var sf = this; 
    	Ext.get(document.documentElement).un("mousemove", sf.onMouseMove, sf);
    	Ext.get(document.documentElement).un("mouseup", sf.onMouseUp, sf);
    	if(sf.proxy){
    		sf.wrap.moveTo(sf.proxy.getX(),sf.proxy.getY());
	    	sf.proxy.hide();
    	}
    },
    onMouseMove : function(e){
    	e.stopEvent();
    	var sl = document[Ext.isStrict?'documentElement':'body'].scrollLeft;
    	var st = document[Ext.isStrict?'documentElement':'body'].scrollTop;
    	var sw = sl + this.screenWidth;
    	var sh = st + this.screenHeight;
    	var tx = e.getPageX()+this.relativeX;
    	var ty = e.getPageY()+this.relativeY;
    	if(tx<=sl) tx =sl;
    	if((tx+this.width)>= (sw-3)) tx = sw - this.width - 3;
    	if(ty<=st) ty =st;
    	if((ty+this.height)>= (sh-3)) ty = Math.max(sh - this.height,0);
    	this.proxy.moveTo(tx,ty);
    },
    showLoading : function(){
    	this.body.update('正在加载...');//TODO:多语言
    	this.body.setStyle('text-align','center');
    	this.body.setStyle('line-height',5);
    },
    clearLoading : function(){
    	this.body.update('');
    	this.body.setStyle('text-align','');
    	this.body.setStyle('line-height','');
    },
    initProxy : function(){
    	var sf = this; 
    	var p = '<DIV class="s-dlg-proxy" onselectstart="return false;" unselectable="on"><div class="s-dlg-proxy-body"></div></DIV>'
    	sf.proxy = Ext.get(Ext.DomHelper.insertFirst(Ext.getBody(),p));
    	var xy = sf.wrap.getXY();
    	sf.proxy.setWidth(sf.wrap.getWidth());
    	sf.proxy.setHeight(sf.wrap.getHeight());
    	sf.proxy.setLocation(xy[0], xy[1]);
    },
    onCloseClick : function(e){
        e.stopEvent();
    	this.close(); 	
    },
    hide : function(){
        this.wrap.hide();
        if(this.modal) $S.Cover.uncover(this.wrap)
    },
    show : function(){
        this.wrap.show();
        if(this.modal) $S.Cover.cover(this.wrap)
    },
    close : function(){
        if(this.closeable) {
            $S.WindowManager.remove(this);
            this.destroy(); 
            this.fireEvent('close', this)
        }else{
            $S.showWarningMessage('提示','当前窗口正在执行后台程序,无法关闭!',null, 350);
        }
    },
    destroy : function(){
//    	$S.focusWindow = null;
    	var wrap = this.wrap;
    	if(!wrap)return;
    	if(this.proxy) this.proxy.remove();
    	if(this.modal) $S.Cover.uncover(this.wrap);
        for(var key in this.cmps){
            var cmp = this.cmps[key];
            if(cmp.destroy){
                try{
                    cmp.destroy();
                }catch(e){
                    alert('销毁window出错: ' + e)
                }
            }
        }
    	$S.Window.superclass.destroy.call(this);
    	delete this.title;
    	delete this.head;
    	delete this.body;
        delete this.closeBtn;
        delete this.proxy;
        wrap.remove();
//        var sf = this;
//        setTimeout(function(){
//        	for(var key in sf.cmps){
//        		var cmp = sf.cmps[key];
//        		if(cmp.destroy){
//        			try{
//        				cmp.destroy();
//        			}catch(e){
//        				alert('销毁window出错: ' + e)
//        			}
//        		}
//        	}
//        },10)
    },
    /**
     * 窗口加载.
     * 
     * @param {String} url  加载的url
     * @param {Object} params  加载的参数
     */
    load : function(url,params){
//    	var cmps = $S.CmpManager.getAll();
//    	for(var key in cmps){
//    		this.oldcmps[key] = cmps[key];
//    	}
        var pa = params||{};
        pa['_sph'] = $S.viewHeight;
        pa['_spw'] = $S.viewWidth;
        pa['SUK'] = $S.suk;
    	Ext.Ajax.request({
			url: url,
            method: 'POST',
			params:pa||{},
		   	success: this.onLoad.createDelegate(this)
		});		
    },
    onLoad : function(response, options){
    	if(!this.body) return;
    	this.clearLoading();
    	var html = response.responseText;
    	var res
    	try {
            res = Ext.decode(response.responseText);
        }catch(e){}
        if(res && res.success == false){
        	if(res.error){
        		$S.manager.fireEvent('ajaxfailed', $S.manager, options.url,options.para,res);
                var st = res.error.stackTrace;
                st = (st) ? st.replaceAll('\r\n','</br>') : '';
                if(res.error.message) {
                    $S.showErrorMessage('错误', res.error.message+'</br>'+st,null,400);//TODO:多语言
                }else{
                    $S.showErrorMessage('错误', st,null,400);
                }   
            }
            return;
        }
    	var sf = this
    	this.body.update(html,true,function(){
//	    	var cmps = $S.CmpManager.getAll();
//	    	for(var key in cmps){
//	    		if(sf.oldcmps[key]==null){	    			
//	    			sf.cmps[key] = cmps[key];
//	    		}
//	    	}
	    	sf.fireEvent('load',sf);
            sf.focus();
    	},this);
    }
});
/**
 * 警告窗口
 * 
 * @param {String} title 标题
 * @param {String} msg 内容
 * @param {Function} callback 回调函数
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showWarningMessage = function(title, msg,callback,width){
	return $S.showTypeMessage(title, msg, width,'s-dlg-warning',callback);
}
/**
 * 信息窗口
 * 
 * @param {String} title 标题
 * @param {String} msg 内容
 * @param {Function} callback 回调函数
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showInfoMessage = function(title, msg,callback,width){
	return $S.showTypeMessage(title, msg, width, 's-dlg-info',callback);
}
/**
 * 错误窗口
 * 
 * @param {String} title 标题
 * @param {String} msg 内容
 * @param {Function} callback 回调函数
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showErrorMessage = function(title,msg,callback,width){
	return $S.showTypeMessage(title, msg, width,'s-dlg-error',callback);
}

$S.showTypeMessage = function(title, content,width,cls,callback){
    var html = ['<div class="s-dlg-title">',title,'</div>','<div class="s-dlg-content">',content,'</div>']
	return $S.showOkWindow(cls,html.join(''), width||340,callback);	
} 
/**
 * 确定窗口.
 * 
 * @param {String} title 标题
 * @param {String} msg 内容
 * @param {Function} okfun 确定的callback
 * @param {Function} nofun 取消的callback
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showConfirm = function(title, content, yesFun,noFun, width){
    var html = ['<div class="s-dlg-title">',title,'</div>','<div class="s-dlg-content">',content,'</div>']
    return $S.showYesNoWindow('s-dlg-confirm', html.join(''), yesFun,noFun, width||340);  	
}
/**
 * 带确定取消按钮的窗口.
 * 
 * @param {String} cls cls
 * @param {String} html html
 * @param {Function} yesfun 确定的callback
 * @param {Function} nofun 取消的callback
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showYesNoWindow = function(cls, html, yesFun,noFun,width){
    var id = Ext.id();
    var wid = 's-win-' + id;
    var yid = 's-yes-'+id;
    var nid = 's-no-'+id;
    var cid = 's-cancle-'+id;
    var cmp = new $S.Window({id:wid,autoCenter:false, className:cls,width:width,hasCloseBtn:true});
    if(html){
        cmp.body.update(html+ '<div class="s-dlg-buttons">'+$S.Button.getTemplate(yid,'是', 70,'s-btn-b')+$S.Button.getTemplate(nid,'否',70)+'</div>',true,function(){
            cmp.center();
            var yesBtn = $(yid);
            var noBtn = $(nid);
            cmp.cmps[yid] = yesBtn;
            cmp.cmps[nid] = noBtn;
            yesBtn.on('click',function(){
            	if(yesFun)yesFun.call(this,cmp);
            });
            noBtn.on('click',function(){
            	if(noFun) {
                    noFun.call(this,cmp)
                }else{
                    cmp.close();                        
                }
            });
        });
    }
    return cmp;
}
/**
 * 带确定按钮的窗口.
 * 
 * @param {String} cls cls
 * @param {String} html html
 * @param {Function} okfun 确定的callback
 * @param {int} width 宽度
 * @return {Window} 窗口对象
 */
$S.showOkWindow = function(cls,html,width,callback){
    var id = Ext.id();
    var wid = 's-win-' + id;
    var oid = 's-ok-'+id;
	var btnhtml = $S.Button.getTemplate(oid,'确 定',70);
	var cmp = new $S.Window({id:wid,autoCenter:false,className:cls, width:width,hasCloseBtn:false});
	if(html){
		cmp.body.update(html+ '<div class="s-dlg-buttons">'+btnhtml+'</div>',true,function(){
            cmp.center();
			var btn = $(oid);
            cmp.cmps[oid] = btn;
            btn.on('click',function(){
                if(callback){
                    callback.call(this,cmp);
                }else{
                    cmp.close()                        
                }
            });
            btn.focus.defer(10,btn);
		});
	}
	return cmp;
}
/**
 * @class Aurora.Lov
 * @extends Aurora.TextField
 * <p>Lov 值列表组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Lov = Ext.extend($S.ComboBox,{
    constructor: function(config) {
        this.isWinOpen = false;
        this.fetching = false;
        this.needFetch = true;
        this.context = config.context||'';
        $S.Lov.superclass.constructor.call(this, config);        
    },
    initComponent : function(config){
        $S.Lov.superclass.initComponent.call(this,config);
        this.plusTtrigger = this.wrap.child('div[stype=plusTrigger]');
        this.para = {};
        if(!Ext.isEmpty(this.lovUrl)){
            this.lovUrl = this.processParmater(this.lovUrl);
        }
    },
    processListener: function(ou){
        $S.Lov.superclass.processListener.call(this, ou);
        if(this.plusTtrigger)this.plusTtrigger[ou]('click',this.onPlusTriggerClick, this, {preventDefault:true});
    },
    processParmater:function(url){
        var li = url.indexOf('?')
        if(li!=-1){
            this.para = Ext.urlDecode(url.substring(li+1,url.length));
            return url.substring(0,li);
        } 
        return url;
    },
    initEvents : function(){
        $S.Lov.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event beforefetch
         * fetch前事件.
         * @param {Aurora.Lov} lov 当前lov
         * @param {Object} o 查询参数. 
         */
        'beforefetch',
        /**
         * @event beforecommit
         * commit前事件.
         * @param {Aurora.Record} r1 当前lov绑定的Record
         * @param {Aurora.Record} r2 选中的Record. 
         */
        'beforecommit',
        /**
         * @event commit
         * commit事件.
         * @param {Aurora.Lov} lov 当前Lov组件.
         * @param {Aurora.Record} r1 当前lov绑定的Record
         * @param {Aurora.Record} r2 选中的Record. 
         */
        'commit',
        /**
         * @event beforetriggerclick
         * 点击弹出框按钮之前的事件。
         * @param {Aurora.Lov} lov 当前Lov组件.
         */
        'beforetriggerclick');
    },
    onTriggerClick : function(e){
    	e.stopEvent();
    	if(this.fireEvent('beforetriggerclick',this)){
            this.collapse();
    		this.showLovWindow();
    	}
    },
    setHeight: function(h){
        $S.Lov.superclass.setHeight.call(this, h);
        if(this.plusTtrigger)this.plusTtrigger.setStyle("height",(h-2)+"px");
    },
    setWidth: function(w){
        $S.Lov.superclass.setWidth.call(this,w);
        //if(this.plusTtrigger) this.el.setStyle("width",(w-52)+"px");
    }, 
    bind : function(ds, name){
        $S.Lov.superclass.bind.call(this,ds,name);
        this.processPlusTrigger(this.record)
    },
    onUpdate : function(ds, record, name, value){
        $S.Lov.superclass.onUpdate.call(this,ds, record, name, value);
        this.processPlusTrigger(record)
    },
    processPlusTrigger : function(record){
        var ptr = $S.getFunction(this.plusTriggerRenderer)
        if(ptr){
            ptr.call(window,this, record);
        }
    },
    showPlusTrigger : function(){
        if(this.plusTtrigger) {
            this.plusTtrigger.setStyle('display','');
            var w = this.wrap.getWidth();
            if(w-53>0)this.el.setStyle("width",(w-53)+"px");
        }
    },
    hidePlusTrigger : function(){
        if(this.plusTtrigger) {
            var w = this.wrap.getWidth();
            this.plusTtrigger.setStyle('display','none');
            if(w-30>0)this.el.setStyle("width",(w-30)+"px");
        }
    },
    onPlusTriggerClick : function(e){
        e.stopEvent();
        this.collapse();
        var pth = $S.getFunction(this.plusTriggerHandler);
        if(pth){
            pth.call(window,this);
        }
    },
    onBlur : function(e){
        var ov,nv;
        if(this.record) ov = this.record.get(this.binder.name); 
        $S.Lov.superclass.onBlur.call(this,e);
        nv = this.getRawValue();
        if(ov != nv && !Ext.isEmpty(this.fetchUrl))
            this.fetchRecord();
    },
    //改用onBlur 因为如果是用全角输入,好像就不触发change事件了
//    onChange : function(e){
//        if(this.fetchRemote == true)
//			this.fetchRecord();
//    },
    onKeyDown : function(e){
        if(this.isWinOpen)return; 
        $S.Lov.superclass.onKeyDown.call(this,e);
    },
    canHide : function(){
        return this.isWinOpen == false
    },
    close : function(){
        if(this.win) this.win.close();
    },
    hide : function(){
        this.win.hide();
    },
    show : function(){
        this.win.show();
    },
    commit:function(r,lr,mapping){
        this.close();
//        this.setRawValue('')
        var record = lr ? lr : this.record;
        if(record && r){
            this.fireEvent('beforecommit', this, record, r)
            var mapping = mapping || this.getMapping();
            for(var i=0;i<mapping.length;i++){
                var map = mapping[i];
//                record.set(map.to,r.get(map.from)||'');//如果是0,就为变成空
                record.set(map.to,r.get(map.from));
            }
        }
//        else{
//          this.setValue()
//        }
        
        this.fireEvent('commit', this, record, r)
    },
    getMapping: function(){
        var mapping
        if(this.record){
            var field = this.record.getField(this.binder.name);
            if(field){
                mapping = field.get('mapping');
            }
        }
        return mapping ? mapping : [{from:this.binder.name,to:this.binder.name}];
    },
    afterClose: function(){
        this.isWinOpen = false;
        this.win = null;
        if(!Ext.isIE6 && !Ext.isIE7){//不知什么地方会导致冲突,ie6 ie7 会死掉 
            this.focus();
        }else{
        	var sf = this;
        	setTimeout(function(){sf.focus()},10)	
        }
    },
    getLovPara : function(){
        var para = Ext.apply({},this.para);
        var field;
        if(this.record) field = this.record.getField(this.binder.name);
        if(field){
            var lovpara = field.get('lovpara'); 
            if(lovpara)Ext.apply(para,lovpara);
        }
        return para;
    },
    fetchRecord : function(){
        if(this.disabeld == true) return;
        this.fetching = true;
        var v = this.getRawValue(),url = this.fetchUrl;
        
//        if(!Ext.isEmpty(this.fetchUrl)){
//            url = this.context + 'svc/'+this.lovUrl+'/query&_ps=1&_pn=1&'+ Ext.urlEncode(this.getLovPara());
//        }
        var record = this.record;
        record.isReady=false;
        var p = {};
        
        var mapping = this.getMapping();
        for(var i=0;i<mapping.length;i++){
            var map = mapping[i];           
            if(this.binder.name == map.to){
                p[map.from]=v;
            }
            record.set(map.to,'');          
        }
        if(Ext.isEmpty(v)) {
            this.fetching = false;
            record.isReady=true;
            return;
        }
        if(this.fetchField) p[this.fetchField] = v;
        this.setRawValue('正在查询...');
        this.fireEvent('beforefetch', this, p)
        $S.request({url:url, para:p, success:function(res){
            var r = new $S.Record({});
            if(res.result){
                var datas = [].concat(res.result);
                var aft = $S.getFunction(this.processData);
                if(aft) datas = aft.call(window,p,datas);
                if(datas.length>0){
                    var data = datas[0];
                    r = new $S.Record(data);
                }
            }
            this.fetching = false;
            this.commit(r,record,mapping);
            record.isReady=true;
        }, error:this.onFetchFailed, scope:this});
    },
    onFetchFailed: function(res){
        this.fetching = false;
    },
    showLovWindow : function(){        
        if(this.fetching||this.isWinOpen||this.disabled) return;       
        var v = this.getRawValue();
        this.blur();
        var url,mapping = this.getMapping(),para ={};
        for(var i=0;i<mapping.length;i++){
            var map = mapping[i];
            if(map.para === "true"){
                para[map.to] = this.record.get(map.to);
            }
        }
        para = Ext.apply(para,this.getLovPara());        
        var lp = Ext.urlEncode(para);
        if(!Ext.isEmpty(this.lovUrl)){
            url = this.lovUrl+'?' +lp;
        }
        if(url) {
	        this.isWinOpen = true;
            this.win = new $S.Window({title:this.title||'Lov', url:url+ (lp!='' ? '&' : '') + "_lid="+this.id+"&key="+encodeURIComponent(v),height:this.lovHeight||400,width:this.lovWidth||400});
            this.win.on('close',this.afterClose,this);
        }
    },
    onInputClick : function(e){}
});
/**
 * @class Aurora.TextArea
 * @extends Aurora.Field
 * <p>TextArea组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.TextArea = Ext.extend($S.Field,{
	constructor: function(config) {
        $S.TextArea.superclass.constructor.call(this, config);        
    },
    processKeyDown : function(e){},
    initEvents : function(){
    	$S.TextArea.superclass.initEvents.call(this);    	
    },
    initElements : function(){
    	this.el= this.wrap;
    }
//    ,setRawValue : function(v){
//        this.el.update(v === null || v === undefined ? '' : v);
//    }
})
/**
 * @class Spark.Grid
 * @extends Spark.Component
 * <p>Grid 数据表格组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Grid = Ext.extend($S.ViewComponent,{
//    focusCss:'',
    nbcls:'s_rq',
    ROW_HEIGHT:25,
    cellCls:'grid-cell',
    constructor: function(config){
        this.overId = null;
        this.selectedId = null;
        this.lockWidth = 0;
        $S.Grid.superclass.constructor.call(this,config);
    },
    initBind : function(){
    	if(this.bt) this.bind(this.bt)
    },
    initFocusElement : function(){
        this.focusEl = this.wb;
    },
    initComponent:function(config){
        $S.Grid.superclass.initComponent.call(this, config);
        var wp = this.wrap;
        this.wb = Ext.get(this.id+'_wrap');
        this.focusEl = this.wb;
        this.fb = this.wb.child('div[stype=grid.fb]');
        if(this.fb){
            this.uf = this.fb.child('div[stype=grid.uf]');
        }
        this.uc = wp.child('div[stype=grid.uc]');
        this.uh = wp.child('div[stype=grid.uh]');
        this.ub = wp.child('div[stype=grid.ub]'); 
        this.uht = wp.child('table[stype=grid.uht]'); 
        
        this.lc = wp.child('div[stype=grid.lc]'); 
        this.lh = wp.child('div[stype=grid.lh]');
        this.lb = wp.child('div[stype=grid.lb]');
        this.lht = wp.child('table[stype=grid.lht]'); 

        this.sp = wp.child('div[stype=grid.spliter]');
        Ext.getBody().insertFirst(this.sp)
        
        this.lockColumns =[],this.unlockColumns = [];
        this.lockWidth = 0,this.unlockWidth = 0;
        for(var i=0,l=this.columns.length;i<l;i++){
            var c = this.columns[i];
            if(c.lock === true){
                this.lockColumns.push(c);
                if(c.hidden !== true) this.lockWidth += c.width;
            }else{
                this.unlockColumns.push(c);
                if(c.hidden !== true) this.unlockWidth += c.width;
            }
        }
        this.columns = this.lockColumns.concat(this.unlockColumns);
        this.initTemplate();
    },
    processListener: function(ou){
    	$S.Grid.superclass.processListener.call(this, ou);
        if(this.listenMouseWheel)this.wb[ou]('mousewheel',this.onMouseWheel,this);
        this.wrap[ou]("mouseover", this.onMouseOver, this)
                 [ou]("mouseout", this.onMouseOut, this)
                 [ou]('mousedown',this.onMouseDown,this);
        this.ub[ou]('scroll',this.syncScroll, this)
//               [ou]('mousedown',this.onMouseDown, this)
               [ou]('dblclick',this.onDblclick, this);
        if(this.uht) this.uht[ou]('mousemove',this.onUnLockHeadMove, this);
        if(this.uh) {
        	this.uh[ou]('mousedown', this.onHeadMouseDown,this)
        	       [ou]('click', this.onHeadClick,this);
        }
        if(this.lb){
            this.lb[ou]('dblclick',this.onDblclick, this);
//                   [ou]('mousedown',this.onMouseDown, this);
        }
        if(this.lht) this.lht[ou]('mousemove',this.onLockHeadMove, this);
        if(this.lh) {
        	this.lh[ou]('mousedown', this.onHeadMouseDown,this)
        	       [ou]('click', this.onHeadClick,this);
        }
        
    },
    initEvents:function(){
        $S.Grid.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event keydown
         * 键盘按下事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {EventObject} e 鼠标事件对象.
         */
        'keydown',
        /**
         * @event dblclick
         * 鼠标双击事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {Spark.Record} record 鼠标双击所在行的Record对象.
         * @param {Number} row 行号.
         * @param {String} 当前name.
         */
        'dblclick',
        /**
         * @event cellclick
         * 单元格点击事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {Number} row 行号.
         * @param {String} 当前name.
         * @param {Spark.Record} record 鼠标点击所在单元格的Record对象.
         */
        'cellclick',
        /**
         * @event rowclick
         * 行点击事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {Number} row 行号.
         * @param {Spark.Record} record 鼠标点击所在行的Record对象.
         */
        'rowclick',
        /**
         * @event editorShow
         * 编辑器显示后触发的事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {Editor} grid 当前Editor组件.
         * @param {Number} row 行号.
         * @param {String} 当前name.
         * @param {Spark.Record} record 鼠标点击所在行的Record对象.
         */
        'editorshow',
        /**
         * @event nexteditorshow
         * 切换下一个编辑器的事件.
         * @param {Spark.Grid} grid 当前Grid组件.
         * @param {Number} row 行号.
         * @param {String} 当前name.
         */
        'nexteditorshow');
    },
    syncScroll : function(){
        this.hideEditor();
        if(this.uh) this.uh.dom.scrollLeft = this.ub.dom.scrollLeft;
        if(this.lb) this.lb.dom.scrollTop = this.ub.dom.scrollTop;
        if(this.uf) this.uf.dom.scrollLeft = this.ub.dom.scrollLeft;
    },
    //监听焦点对象的键盘事件
    onFocusKeyDown : function(e){
        var key = e.getKey(),ds = this.dataset;
        if(key == 38 || key == 40 || key == 33 || key == 34) {
            if(ds.loading == true) return;
            e.stopEvent();
            switch(e.getKey()){
                case 33://PAGEUP
                    ds.prePage();
                    break;
                case 34://PAGEDOWN
                    ds.nextPage();
                    break;
                case 38://UP
                    ds.pre();
                    break;
                case 40://DOWN
                    ds.next();
                    break;
            }
        }
        this.fireEvent('keydown', this, e)
    },
    processDataSetLiestener: function(ou){
        var ds = this.dataset;
        if(ds){
        	ds[ou]('ajaxfailed', this.onAjaxFailed, this);
            ds[ou]('metachange', this.onRefresh, this);//TODO: remove ?
            ds[ou]('update', this.onUpdate, this);
            ds[ou]('reject', this.onUpdate, this);
            ds[ou]('add', this.onAdd, this);
            ds[ou]('submit', this.onBeforSubmit, this);
            ds[ou]('submitfailed', this.onAfterSubmit, this);
            ds[ou]('submitsuccess', this.onAfterSubmit, this);
            ds[ou]('query', this.onBeforeLoad, this);
            ds[ou]('load', this.onLoad, this);
            ds[ou]('loadfailed', this.onAjaxFailed, this);
            ds[ou]('valid', this.onValid, this);
            ds[ou]('beforeremove', this.onBeforeRemove, this);
            ds[ou]('remove', this.onRemove, this);
            ds[ou]('clear', this.onLoad, this);
            ds[ou]('refresh',this.onRefresh,this);
            ds[ou]('fieldchange', this.onFieldChange, this);
            ds[ou]('indexchange', this.onIndexChange, this);
            ds[ou]('select', this.onSelect, this);
            ds[ou]('unselect', this.onUnSelect, this);
        }
    },
    bind : function(ds){
        if(typeof(ds)==='string'){
            ds = $(ds);
            if(!ds) return;
        }
        var sf = this;
        if(sf.autoPageSize == true){
            var ht = sf.ub.getStyle('height');
            ht = parseInt(ht.replaceAll('px',''));
            var pz = Math.floor((ht-23)/24);
            ds.psize = pz;
        }
        sf.dataset = ds;
        sf.processDataSetLiestener('on');
        this.onLoad();
        if(ds.current())this.onIndexChange(ds,ds.current())
//        Ext.onReady(function(){
//            sf.onLoad();
//        })
    },
    
    //REFINED
    initTemplate : function(){
    	//rowbox 
        this.rowBoxTpl = new Ext.Template('<td stype="{stype}" class="grid-rowbox" align="center" rid="{rid}">');
        //多选单选框
        this.cbTpl = new Ext.Template('<div class="{cellcls}" id="'+this.id+'_{name}_{rid}"><div class="s-ckb-box"></div></div>');        
        //行号        
        this.rowNumTpl = new Ext.Template('<td class="grid-rownumber" stype="rownum" rid="{rid}">');
        //普通单元格
        this.cellTpl = new Ext.Template('<td {tip} style="{visibility}" {align} dataindex="{name}" stype="cell" id="'+this.id+'_{name}_{rid}" class="{gridCell} {className}" rid="{rid}">');
    },
    getCheckBoxStatus: function(record, name) {
        var field = this.dataset.getField(name);
        if(field){
            var value = record.data[name];
            return (value && value == field.getPro('checkedValue')) ? 's-ckb-c' : 's-ckb-u';
        }
    },
    createTemplateData : function(col,record){
    	return {
            width:col.width-3,
            rid:record.id,
            gridCell:this.cellCls,
            tip: col.tip === 'true' ? 'tip="true"' : '',
            visibility: col.hidden === true ? 'visibility:hidden' : '',
            name:col.name
        }
    },
    
    //REFINED 
    //TODO:spark cell结构和aurora已经有差异
    createCell : function(col,record,includeTd){
        var data = this.createTemplateData(col,record),tdTpl = this.cellTpl,xtype = col.type,editor = this.getEditor(col,record),cls = '',html = '';
        if(editor!=''){
        	var edi = $S.CmpManager.get(editor);
        	//这里对于checkbox可能会存在问题
            if(edi && (edi instanceof $S.CheckBox)){
                xtype = 'cellcheck';
            }
        }
        if(xtype == 'rowcheck'||xtype == 'rowradio'){
        	var readonly="";
        	if(!this.dataset.execSelectFunction(record))readonly="_ro"
            tdTpl = this.rowBoxTpl;
            html = this.cbTpl.applyTemplate(Ext.apply(data,{
                stype: xtype == 'rowcheck'?'checkbox':'radio',
                cellcls: xtype == 'rowcheck'?'grid-ckb s-ckb'+readonly+'-u':'grid-radio s_radio'+readonly+'_u'
            }))
        } else if(xtype == 'cellcheck'){
            html = this.cbTpl.applyTemplate(Ext.apply(data,{
                align:'center',
                cellcls: 'grid-ckb ' + this.getCheckBoxStatus(record, col.name)
            }))
        } else if(xtype == 'rownumber'){
        	tdTpl = this.rowNumTpl;
            html = this.renderText(record,col,record.data[col.name],col.name)
        } else {
            var field = record.getField(col.name);
            if(field && Ext.isEmpty(record.data[col.name]) && record.isNew == true && field.get('required') == true){
                cls = cls + ' ' + this.nbcls
            }
            data = Ext.apply(data,{
                align:(col.align && col.align!= 'left') ? ('align="'+col.align+'"') : '',
                className:cls + ' ' + (col.className ? col.className : '')
            })
            html = this.renderText(record,col,record.data[col.name],col.name);
        }
        
        return includeTd==true ?[tdTpl.applyTemplate(data),html,'</td>'].join('') : html;
    },
    createRow : function(type, row, cols, item){
        var sb = [],css=this.parseCss(this.renderRow(item,row));
        sb.push('<tr height="'+this.ROW_HEIGHT+'" id="'+this.id+'$'+type+'-'+item.id+'" class="'+ (this.stripeRows ? (row % 2==0 ? '' : 'row-alt') : '')+css.cls+'"'+'style="'+css.style+'">');
        
        for(var i=0,l=cols.length;i<l;i++){
            var c = cols[i];
            sb.push(this.createCell(c,item,true))           
        }
        sb.push('</tr>');
        return sb.join('');
    },
    parseCss:function(css){
    	var style="",cls="";
    	if(Ext.isArray(css)){
    		for(var i=0;i<css.length;i++){
    			var _css=this.parseCss(css[i]);
    			style+=";"+_css.style;
    			cls+=" "+_css.cls;
    		}
    	}else if(typeof css=="string"){
    		isStyle=!!css.match(/^([^,:;]+:[^:;]+;)*[^,:;]+:[^:;]+;*$/);
    		cls=isStyle?"":css;
			style=isStyle?css:"";
    	}
    	return {style:style,cls:cls}
    	
    },
    //TODO: 没有value的时候不再更新是否能提高效率?
    renderText : function(record,col,value,name){
        var renderer = col.renderer;
        if(renderer){//&&!Ext.isEmpty(value)  去掉对value是否为空的判断
            var rder = $S.getFunction(renderer);
            if(rder == null){
                alert("未找到"+renderer+"方法!")
                return value;
            }
            value = rder.call(window,value,record, name);
            return (value === '') ? '&#160' : value;
        }
        return (value == null||value == '') ? '&#160' : $S.escapeHtml(value);
    },
    renderRow : function(record,rowIndex){
    	var renderer = this.rowRenderer,css=null;
        if(renderer){
            var rder = $S.getFunction(renderer);
            if(rder == null){
                alert("未找到"+renderer+"方法!")
                return css;
            }
            css = rder.call(window,record, rowIndex);
            return !css? '' : css;
        }
        return css ;
    },
    createTH : function(cols){
        var sb = [];
        sb.push('<tr class="grid-hl"');
        if(Ext.isIE6 || Ext.isIE7){
            sb.push(' style="display:none"');
        }
        sb.push('>');
        for(var i=0,l=cols.length;i<l;i++){
            var w = cols[i].width;
            if(cols[i].hidden === true) w = 0;
            sb.push('<th dataindex="'+cols[i].name+'" style="height:0px;width:'+w+'px"></th>');
        }
        sb.push('</tr>');
        return sb.join('');
    },
    onBeforeRemove : function(){
        $S.Masker.mask(this.wb,'正在删除数据...');
    },
    onBeforeLoad : function(){
        $S.Masker.mask(this.wb,'正在查询数据...');
    },
    onBeforSubmit : function(ds){
    	$S.Masker.mask(this.wb,'正在提交数据...');
    },
    onAfterSubmit : function(){
        $S.Masker.unmask(this.wb);
    },
    preLoad : function(){},
    onLoad : function(){
        this.selectRecord = null;
    	this.clearDomRef();
    	this.preLoad();
        var cb = Ext.fly(this.wrap).child('div[stype=grid.headcheck]');
        if(this.selectable && this.selectionModel=='multiple')this.setCheckBoxStatus(cb,false);
        if(this.lb)
        this.renderLockArea();
        this.renderUnLockAread();
//        if(focus !== false) this.focus.defer(10,this);//获取数据后的获得焦点,会引起其他编辑器无法编辑
        this.drawFootBar();
        $S.Masker.unmask(this.wb);
    },
    clearDomRef : function(){
    	this.selectlockTr = null;
        this.selectUnlockTr = null;
        
    },
    onAjaxFailed : function(res,opt){
        $S.Masker.unmask(this.wb);
    },
    onMouseWheel : function(e){
        e.stopEvent();
        if(this.editing == true) return;
    	var delta = e.getWheelDelta();
        if(delta > 0){
            this.dataset.pre();
        } else if(delta < 0){
            this.dataset.next();
        }
    },
    renderLockArea : function(){
        var sb = [];var cols = this.lockColumns;
        sb.push('<TABLE cellSpacing="0" stype="grid.lbt" cellPadding="0" border="0"  width="'+this.lockWidth+'"><TBODY>');
        sb.push(this.createTH(cols));
        for(var i=0,l=this.dataset.datas.length;i<l;i++){
            sb.push(this.createRow('l', i, cols, this.dataset.getAt(i)));
        }
        sb.push('</TBODY></TABLE>');
        sb.push('<DIV style="height:17px"></DIV>');
        this.lb.update(sb.join(''));
        this.lbt = this.lb.child('table[stype=grid.lbt]'); 
    },
    renderUnLockAread : function(){
        var sb = [];var cols = this.unlockColumns;
        sb.push('<TABLE cellSpacing="0" stype="grid.ubt" cellPadding="0" border="0" width="'+this.unlockWidth+'"><TBODY>');//width="'+this.unlockWidth+'" ??
        sb.push(this.createTH(cols));
        for(var i=0,l=this.dataset.datas.length;i<l;i++){
            sb.push(this.createRow('u', i, cols, this.dataset.getAt(i)));
        }
        sb.push('</TBODY></TABLE>');
        this.ub.update(sb.join(''));
        this.ubt = this.ub.child('table[stype=grid.ubt]'); 
    },
    isOverSplitLine : function(x){
        var v = 0;      
        var isOver = false;
        this.overColIndex = -1;
        var columns = this.columns;
        for(var i=0,l=columns.length;i<l;i++){
            var c = columns[i];
            if(c.hidden !== true) v += c.width;
            if(x < v+3 && x > v-3 && c.resizable != false){
                isOver = true;
                this.overColIndex = i;
                break;
            }
        }
        return isOver;
    },
    onRefresh : function(){
        this.onLoad();
    },
    onIndexChange:function(ds, r){
        var index = this.getDataIndex(r.id);
        if(index == -1)return;
        if(r != this.selectRecord){
            this.selectRow(index, false);
        }
    },    
    onAdd : function(ds,record,index){
        if(this.lb)
        var sb = [];var cols = [];
        var v = 0;
        var columns = this.columns;
        var row = this.dataset.datas.length-1;
        var css = this.parseCss(this.renderRow(record,row));
        if(this.lbt){
            var ltr = document.createElement("tr");
            ltr.id=this.id+'$l'+'-'+record.id;
            ltr.className= (this.stripeRows ? (row % 2==0 ? '' : 'row-alt') : '') +' '+css.cls;
            Ext.fly(ltr).set({'style':css.style,height:this.ROW_HEIGHT});
            for(var i=0,l=columns.length;i<l;i++){
                var col = columns[i];
                if(col.lock === true){
                    var td = document.createElement("td");
                    if(col.type == 'rowcheck' || col.type == 'rowradio') {
                        Ext.fly(td).set({
                        	'rid':record.id,
                        	'stype':col.type
                        })
                        td.className = 'grid-rowbox';
                        td.align = 'center';
                    } else if(col.type == 'rownumber') {
                        Ext.fly(td).set({
                        	'rid':record.id,
                        	'stype':'rownum'
                        })
                        td.className = 'grid-rownumber';
                    }else{
                    	td.id = this.id+'_' + col.name + '_' + record.id;
                        col.hidden === true ? (td.style.visibility='hidden') : '';
                        td.style.textAlign= col.align||'left';
                        td.className = this.cellCls + ' ' + (col.className ? col.className : '');    
                        Ext.fly(td).set({
                        	'dataindex':col.name,
                        	'rid':record.id,
                        	'stype':'cell'
                        });
                    }
                    var cell = this.createCell(col,record,false);
                    td.innerHTML = cell;
                    ltr.appendChild(td);
                }
            }
            this.lbt.dom.tBodies[0].appendChild(ltr);
        }
        
        var utr = document.createElement("tr");
        utr.id=this.id+'$u'+'-'+record.id;
        utr.className= (this.stripeRows ? (row % 2==0 ? '' : 'row-alt') : '') +' '+css.cls;
        Ext.fly(utr).set({'style':css.style,height:this.ROW_HEIGHT});
        for(var i=0,l=columns.length;i<l;i++){
            var col = columns[i];
            if(col.lock !== true){
                var td = document.createElement("td");
                td.id = this.id+'_' + col.name + '_' + record.id;
                col.hidden === true ? (td.style.visibility='hidden') : '';
                td.style.textAlign=col.align||'left';
                td.className = this.cellCls + ' ' + (col.className ? col.className : '');
                Ext.fly(td).set({
                    'dataindex':col.name,
                    'rid':record.id,
                    'stype':'cell'
                })
                var cell = this.createCell(col,record,false);
                td.innerHTML = cell;
                utr.appendChild(td);
            }
        }
        this.ubt.dom.tBodies[0].appendChild(utr);
    	this.setSelectStatus(record);
    },
    renderEditor : function(div,record,c,editor){
    	var cell = this.createCell(c,record,false);
    	div.parent().update(cell);
    },
    onUpdate : function(ds,record, name, value){
        this.setSelectStatus(record);
        Ext.each(this.columns,function(c){
            var td = Ext.get(this.id+'_'+c.name+'_'+record.id);
            if(td)
            if(c.name === name) {
                td.update(this.renderText(record,c, record.get(c.name), name));
            }else {
                if(c.renderer) {
                    var text = this.renderText(record,c, record.get(c.name), name);
                    if(text) td.update(text);
                }                            
            }
        },this)
        this.drawFootBar(name);
    },
    onValid : function(ds, record, name, valid){
        var c = this.findColByName(name);
//      if(c&&c.editor){
        if(c){
            var div = Ext.get(this.id+'_'+name+'_'+record.id);
            if(div) {
                if(valid == false){
                    div.addClass('s_ivd');
                }else{
                    div.removeClass([this.nbcls,'s_ivd']);
                }
            }
        }
    },
    onRemove : function(ds,record,index){
        var lrow = Ext.get(this.id+'$l-'+record.id);
        if(lrow)lrow.remove();
        
        var urow = Ext.get(this.id+'$u-'+record.id);
        if(urow)urow.remove();
        
        if(this.selectUnlockTr == Ext.get(this.id+'$u-'+record.id) && this.selectlockTr == Ext.get(this.id+'$l-'+record.id))
        this.clearDomRef();
        $S.Masker.unmask(this.wb);
    },
    onClear : function(){
        
    },
    onFieldChange : function(ds, record, field, type, value){
        switch(type){
           case 'required':
               var div = Ext.get(this.id+'_'+field.name+'_'+record.id);
               if(div) {
                   (value==true) ? div.addClass(this.nbcls) : div.removeClass(this.nbcls);
               }
               break;
        }
    },
    getDataIndex : function(rid){
        var index = -1;
        for(var i=0,l=this.dataset.datas.length;i<l;i++){
            var item = this.dataset.getAt(i);
            if(item.id == rid){
                index = i;
                break;
            }
        }
        return index;
    },
    onSelect : function(ds,record){
        var cb = Ext.get(this.id+'__'+record.id);
        if(cb && this.selectable && this.selectionModel=='multiple') {
            this.setCheckBoxStatus(cb, true);
            var tr = Ext.get(this.id+'$u-'+record.id);
            if(tr) {
                tr.removeClass('row-current');
                tr.addClass('row-selected');
            }
            tr = Ext.get(this.id+'$l-'+record.id);
            if(tr) {
                tr.removeClass('row-current');
                tr.addClass('row-selected');
            }
        }else{
            this.setRadioStatus(cb,true);
            this.dataset.locate((this.dataset.currentPage-1)*this.dataset.psize + this.dataset.indexOf(record) + 1)
        }
    },
    onUnSelect : function(ds,record){
        var cb = Ext.get(this.id+'__'+record.id);
        if(cb && this.selectable && this.selectionModel=='multiple') {
            this.setCheckBoxStatus(cb, false);
            var tr = Ext.get(this.id+'$u-'+record.id);
            if(tr) tr.removeClass('row-selected');
            tr = Ext.get(this.id+'$l-'+record.id);
            if(tr) tr.removeClass('row-selected');
        }else{
            this.setRadioStatus(cb,false);
        }
    },
    onDblclick : function(e){
        var target = Ext.fly(e.target).findParent('td[stype=cell]');
        if(target){
        	target = Ext.fly(target)
            var rid = target.getAttributeNS("","rid");
            var record = this.dataset.findById(rid);
            var row = this.dataset.indexOf(record);
            var name = target.getAttributeNS("","dataindex");
            this.fireEvent('dblclick', this, record, row, name)
        }
    },
    onMouseDown : function(e) {
        //先判断是不是grid的选择框,如果不是在判断是不是可编辑的cell
    	var target = Ext.fly(e.target);
        var td = target.findParent('td.grid-rowbox');
        if(!td) td = target.findParent('td.rownum');
        if(!td) {
            td = target.findParent('td.'+ this.cellCls);
        }
        if(td){
        	td = Ext.fly(td)
            var stype = td.getAttributeNS("","stype");
            var rid = td.getAttributeNS("","rid");
            if(stype=='cell'){
                this.stopFocus = true;
                var record = this.dataset.findById(rid);
                var row = this.dataset.indexOf(record);
                var name = td.getAttributeNS("","dataindex");
                this.hideEditor();//不隐藏的话,会先显示,再blur触发,这样当前的编辑器无法正常通过editorFunction获取到值
                this.showEditor(row,name);
                this.fireEvent('cellclick', this, row, name, record);
                this.fireEvent('rowclick', this, row, record);
                return;
            }else if (stype == 'rownum'){
                var record = this.dataset.findById(rid);
                var row = this.dataset.indexOf(record);
                if(record.id != this.selectedId) this.selectRow(row);
            } else if(stype=='checkbox'){ 
                var cb = Ext.get(this.id+'__'+rid);
                if(cb.hasClass('s-ckb-ro-u'))return;
                var checked = cb.hasClass('s-ckb-c');
                (checked) ? this.dataset.unSelect(rid) : this.dataset.select(rid);
            }else if(stype=='radio'){
            	var cb = Ext.get(this.id+'__'+rid);
                if(cb.hasClass('s_radio_ro_u'))return;
                this.dataset.select(rid);
            }
        }
        this.focus();
    },
    /**
     * 设置指定name列的标题.
     * 
     * @param {String} name 列的name
     * @param {String} prompt 标题信息
     */
    setColumnPrompt: function(name,prompt){
        var tds = Ext.DomQuery.select('td.grid-hc',this.wrap.dom);
        for(var i=0,l=tds.length;i<l;i++){
            var td = tds[i];
            var dataindex = Ext.fly(td).getAttributeNS("","dataindex");
            if(dataindex==name){
            	var div = Ext.fly(td).child('div');
            	div.update(prompt)
                break;
            }
        }
    },
    /**
     * 设置当前行的编辑器.
     * 
     * @param {String} name 列的name.
     * @param {String} editor 编辑器的id. ''空表示没有编辑器.
     */
    setEditor: function(name,editor){
        var col = this.findColByName(name);
        col.editor = editor;
    },
    getEditor : function(col,record){
        var ed = col.editor||'';
        if(col.editorFunction) {
            var ef = $S.getFunction(col.editorFunction);
            if(ef==null) {
                alert("未找到"+col.editorFunction+"方法!") ;
                return null;
            }
            ed = ef.call(window,record,col.name)
        }
        return ed;
    },
    /**
     * 显示编辑器.
     * @param {Number} row 行号
     * @param {String} name 当前列的name.
     */
    showEditor : function(row, name,callback){
        if(row == -1)return;
        var col = this.findColByName(name);
        if(!col)return;
        var record = this.dataset.getAt(row);
        if(!record)return;
        
        if(record.id != this.selectedId);
        this.selectRow(row);
        this.focusColumn(name);
        var editor = this.getEditor(col,record);
        this.setEditor(name, editor);
        if(editor!='' && ($(editor) instanceof $S.CheckBox)){
            var field = this.dataset.getField(name);
            if(field) {
                var cv = field.getPro('checkedvalue'),uv = field.getPro('uncheckedvalue'),v = record.get(name);
                record.set(name, v == cv ? uv : cv);
            }
        } else if(editor){
            var sf = this;
            setTimeout(function(){
                var dom = document.getElementById(sf.id+'_'+name+'_'+record.id);
                var xy = Ext.fly(dom).getXY();
                var v = record.get(name)
                sf.currentEditor = {
                    record:record,
                    ov:v,
                    name:name,
                    editor:$(editor)
                };
                var ed = sf.currentEditor.editor;
                if(ed){
                    ed.setHeight(Ext.fly(dom).getHeight()-1);//下边为1
                    ed.setWidth(Ext.fly(dom).getWidth()-1);//右边为1
                    ed.isEditor = true;
                    ed.isFireEvent = true;
                    ed.isHidden = false;
                    ed.setVisible(true);
                    ed.move(xy[0],xy[1]);
                    ed.bind(sf.dataset, name);
                    ed.render(record);
                    ed.focus();
                    if(ed.expand){
                        if(ed instanceof $S.Lov){
                            var f = sf.dataset.getField(name);
                            if(!record.get(f.getPro('valueField')))ed.expand();
                        }else{
                            ed.expand();
                        }
                    }
                    sf.editing = true;
                    ed.on('keydown', sf.onEidtorKeyDown,sf);
//                    ed.on('blur',sf.onEditorBlur, sf);
                    Ext.get(document.documentElement).on("mousedown", sf.onEditorBlur, sf);
                    if(callback)callback.call(window,ed)
                }
                sf.fireEvent('editorshow', sf, ed, row, name, record);
            },5)
        }
    },
    onEidtorKeyDown : function(editor,e){
        var keyCode = e.keyCode;
        //esc
        if(keyCode == 27) {
            editor.clearInvalid();
            editor.render(editor.binder.ds.current());
            this.hideEditor();
        }
        //enter
        if(keyCode == 13) {
            this.showNextEditor();
        }
        //tab
        if(keyCode == 9){
            e.stopEvent();
            this.showNextEditor();
        }
    },
    showNextEditor : function(){
        this.hideEditor();
        var callback = function(ed){
//            alert(ed)
        }
        if(this.currentEditor && this.currentEditor.editor){
            var ed = this.currentEditor.editor,ds = ed.binder.ds,
                fname = ed.binder.name,r = ed.record,
                row = ds.datas.indexOf(r),name=null;
            if(row!=-1){
                var cls = this.columns;
                var start = 0;
                for(var i = 0,l = cls.length; i<l; i++){
                    if(cls[i].name == fname){
                        start = i+1;
                    }
                }
                for(var i = start,l = cls.length; i<l; i++){
                    var col = cls[i];
                    var editor = this.getEditor(col,r);
                    if(editor!=''){
                        name =  col.name;
                        break;
                    }
                }
                if(name){
                    this.showEditor(row,name,callback);
                }else{
                    var nr = ds.getAt(row+1);
                    if(nr){
                        for(var i = 0,l = cls.length; i<l; i++){
                            var col = cls[i];
                            var editor = this.getEditor(col,r);
                            if(editor!=''){
                                name = col.name;
                                this.showEditor(row+1,name,callback);
                                break;
                            }
                        }
                    }
                }
            }
            this.fireEvent('nexteditorshow',this, row, name);
        }
    },
    /**
     * 指定行获取焦点.
     * @param {Number} row
     */
    doFocusRow : function(row){
        if(this.focusRow){
            var r = this.ROW_HEIGHT;
            var stop = this.ub.getScroll().top;
            if(row*r<stop){
                this.ub.scrollTo('top',row*r-1)
            }
            if((row+1)*r>(stop+this.ub.getHeight())){//this.ub.dom.scrollHeight
                var st = this.ub.dom.scrollWidth > this.ub.dom.clientWidth ? (row+1)*r-this.ub.getHeight() + 16 : (row+1)*r-this.ub.getHeight();
                this.ub.scrollTo('top',st)
            }
        }
    },
    focusColumn: function(name){
        var r = this.ROW_HEIGHT;
        var sleft = this.ub.getScroll().left;
        var ll = lr = lw = tw = 0;
        for(var i=0,l=this.columns.length;i<l;i++){
            var c = this.columns[i];
            if(c.name && c.name == name) {
//          if(c.name && c.name.toLowerCase() == name.toLowerCase()) {
                tw = c.width;
            }
            if(c.hidden !== true){
                if(c.lock === true){
                    lw += c.width;
                }else{
                    if(tw==0) ll += c.width;
                }
            }
        }
        lr = ll + tw;
        if(ll<sleft){
            this.ub.scrollTo('left',ll)
        }
        if((lr-sleft)>(this.width-lw)){
            this.ub.scrollTo('left',lr  - this.width + lw)
        }
//        this.focus();
    },
    /**
     * 隐藏当前编辑器
     */
    hideEditor : function(){
        if(this.currentEditor && this.currentEditor.editor && this.editing){
            var ed = this.currentEditor.editor;
            //ed.un('blur',this.onEditorBlur, this);
            var candHide = true;
            if(ed.canHide){
                candHide = ed.canHide();
            }
            
            if(candHide) {
                ed.un('keydown', this.onEidtorKeyDown,this);
                Ext.get(document.documentElement).un("mousedown", this.onEditorBlur, this);
                var ed = this.currentEditor.editor;
                ed.setVisible(false);
//                ed.move(-10000,-10000);
                ed.blur();
//                ed.onBlur();
                ed.isFireEvent = false;
                ed.isHidden = true;
                this.editing = false;
            }
        }
    },
    onEditorBlur : function(e){
        if(this.currentEditor && !this.currentEditor.editor.isEventFromComponent(e.target)) {           
            this.hideEditor();
        }
    },
    onLockHeadMove : function(e){
//      if(this.draging)return;
        var hmx = e.xy[0] - this.lht.getXY()[0];
        this.lh.setStyle('cursor', this.isOverSplitLine(hmx) ? "w-resize" : "default");
    },
    onUnLockHeadMove : function(e){
//      if(this.draging)return;
        var lw = 0;
        if(this.uht){
            lw = this.uht.getXY()[0] + this.uht.getScroll().left;
        }
        var hmx = e.xy[0] - lw + this.lockWidth;
        this.uh.setStyle('cursor',this.isOverSplitLine(hmx) ? "w-resize" : "default"); 
    },
    onHeadMouseDown : function(e){
        this.dragWidth = -1;
        if(this.overColIndex == undefined || this.overColIndex == -1) return;
        this.dragIndex = this.overColIndex;
        this.dragStart = e.getXY()[0];
        this.sp.setHeight(this.wrap.getHeight());
        this.sp.setVisible(true);
        this.sp.setStyle("top", this.wrap.getXY()[1]+"px")
        this.sp.setStyle("left", e.xy[0]+"px")
        Ext.get(document.documentElement).on("mousemove", this.onHeadMouseMove, this);
        Ext.get(document.documentElement).on("mouseup", this.onHeadMouseUp, this);
    },
    onHeadClick : function(e){
        var target = Ext.fly(e.target).findParent('td');
        var stype;
        if(target) {
            target = Ext.fly(target)
            stype = target.getAttributeNS("","stype");
            if(stype=='grid.head'){
                var index = target.getAttributeNS("","dataindex");
                var col = this.findColByName(index);
                if(col && col.sortable === true){
                	if(this.dataset.isModified()){
                        $S.showInfoMessage('提示', '有未保存数据!');
                        return;
                	}
                    var d = target.child('div');
                    var of = index;
                    var ot = '';
                    if(!this.currentSortTarget) {
                        var sf = this.dataset.defaultSortField;
                        if(sf){
                            var std = this.uht.child('td[dataindex='+sf+']');
                            if(!std) std = this.lht.child('td[dataindex='+sf+']');
                            if(std) this.currentSortTarget = std.child('div');
                        }
                    }
                    if(this.currentSortTarget){
                        var cst = Ext.fly(this.currentSortTarget)
                        cst.removeClass(['grid-asc','grid-desc']);
                    }
                    this.currentSortTarget = d;
                    if(col.sortType == 'desc') {
                        col.sortType = 'asc'
                        d.removeClass('grid-desc');
                        d.addClass('grid-asc');
                        ot = 'asc';
                    } else{
                        col.sortType = 'desc';
                        d.removeClass('grid-asc');
                        d.addClass('grid-desc');
                        ot = 'desc';
                    }
                    this.dataset.sort(of,ot);
                }
            } else if(stype=='grid.rowcheck'){
                var cb = target.child('div[stype=grid.headcheck]');
                var checked = cb.hasClass('s-ckb-c');
                this.setCheckBoxStatus(cb,!checked);
                if(!checked){
                    this.dataset.selectAll();
                }else{
                    this.dataset.unSelectAll();
                    var cr = this.dataset.current()
                    this.selectRow(this.dataset.indexOf(cr));
                }
            }
        }
    },
    setRadioStatus: function(el, checked){
        if(!checked){
            el.removeClass('s_radio_c');
            el.addClass('s_radio_u');
        }else{
            el.addClass('s_radio_c');
            el.removeClass('s_radio_u');
        }
    },
    setCheckBoxStatus: function(el, checked){
        if(!checked){
            el.removeClass('s-ckb-c');
            el.addClass('s-ckb-u');
        }else{
            el.addClass('s-ckb-c');
            el.removeClass('s-ckb-u');
        }
    },
    setSelectDisable:function(el){
    	if(this.selectable && this.selectionModel=='multiple'){
    		el.removeClass('s-ckb-c');
    		el.removeClass('s-ckb-u');
    		el.addClass('s-ckb-ro-u');
    	}else{
    		el.removeClass('s_radio_c');
    		el.removeClass('s_radio_u');
    		el.addClass('s_radio_ro_u');
    	}
    },
    setSelectEnable:function(el){
    	if(this.selectable && this.selectionModel=='multiple'){
    		el.removeClass('s-ckb-ro-u');
    		el.addClass('s-ckb-u');
    	}else{
    		el.removeClass('s_radio_ro_u');
    		el.addClass('s_radio_u');
    	}	
    },
    setSelectStatus:function(record){
    	if(this.dataset.selectfunction){
	    	var cb = Ext.get(this.id+'__'+record.id);
	    	if(!this.dataset.execSelectFunction(record)){
	    		 this.dataset.unSelect(record);
	    		 this.setSelectDisable(cb)
	    	}else{
	    		 this.setSelectEnable(cb);
	    	}
    	}
    },
    onHeadMouseMove: function(e){
//      this.draging = true
        e.stopEvent();
        this.dragEnd = e.getXY()[0];
        var move = this.dragEnd - this.dragStart;
        var c = this.columns[this.dragIndex];
        var w = c.width + move;
        if(w > 30 && w < this.width) {
            this.dragWidth = w;
            this.sp.setStyle("left", e.xy[0]+"px")
        }
    },
    onHeadMouseUp: function(e){
//      this.draging = false;
        Ext.get(document.documentElement).un("mousemove", this.onHeadMouseMove, this);
        Ext.get(document.documentElement).un("mouseup", this.onHeadMouseUp, this);      
        this.sp.setVisible(false);
        if(this.dragWidth != -1)
        this.setColumnSize(this.columns[this.dragIndex].name, this.dragWidth);
        this.syncScroll();
    },
    /**
     * 根据列的name获取列配置.
     * 
     * @param {String} name 列的name
     * @return {Object} col 列配置对象.
     */
    findColByName : function(name){
        var col;
        for(var i=0,l=this.columns.length;i<l;i++){
            var c = this.columns[i];
            if(c.name && c.name.toLowerCase() === name.toLowerCase()){
                col = c;
                break;
            }
        }
        return col;
    }, 
    /**
     * 选中高亮某行.
     * @param {Number} row 行号
     */
    selectRow : function(row, locate){
        var record = this.dataset.getAt(row);
        if(!record)return;
        this.selectedId = record.id;
        if(this.selectlockTr) this.selectlockTr.removeClass('row-current');
        if(this.selectUnlockTr) this.selectUnlockTr.removeClass('row-current');
        
        this.selectUnlockTr = Ext.get(this.id+'$u-'+record.id);
        if(this.selectUnlockTr && !this.selectUnlockTr.hasClass('row-selected')){
            this.selectUnlockTr.addClass('row-current');
        }
        this.selectlockTr = Ext.get(this.id+'$l-'+record.id);
        if(this.selectlockTr && !this.selectlockTr.hasClass('row-selected')){
            this.selectlockTr.addClass('row-current');
        }
        this.doFocusRow(row);
        
        var r = (this.dataset.currentPage-1)*this.dataset.psize + row+1;
        this.selectRecord = record
        if(locate!==false && r != null) {
//          this.dataset.locate(r);
            this.dataset.locate.defer(1, this.dataset,[r,false]);
        }
    },
    /**
     * 设置某列的宽度.
     * @param {String} name 列的name
     * @param {Number} size 列的宽度
     */
    setColumnSize : function(name, size){
        var columns = this.columns;
        var hth,bth,lw=0,uw=0;
        var wd = "width",px="px";
        for(var i=0,l=columns.length;i<l;i++){
            var c = columns[i];
            if(c.name && c.name === name){
                if(c.hidden === true) return;
                c.width = size;
                if(c.lock !== true){                    
                    hth = this.uh.child('th[dataindex='+name+']');
                    bth = this.ub.child('th[dataindex='+name+']');                  
                }else{                          
                    if(this.lh) hth = this.lh.child('th[dataindex='+name+']');
                    if(this.lb) bth = this.lb.child('th[dataindex='+name+']');
                    
                }
            }
            c.lock !== true ? (uw += c.width) : (lw += c.width);
        }
        var tds = Ext.DomQuery.select('td[dataindex='+name+']',this.wrap.dom);
        for(var i=0,l=tds.length;i<l;i++){
            var td = tds[i];
            var ce = Ext.fly(td).child('DIV.'+ this.cellCls);
            if(ce){
            	var sp =  1;
                Ext.fly(ce).setStyle(wd, Math.max(size-sp,0)+px);
            }
        }
        
        this.unlockWidth = uw;
        this.lockWidth = lw;
        if(hth) hth.setStyle(wd, size+px);
        if(bth) bth.setStyle(wd, size+px);
        if(this.fb){
            var ftd = this.fb.child('th[dataindex='+name+']');
            ftd.setStyle(wd, size+px);
            var uft = this.fb.child('table[stype=fb.ubt]');
            this.uf.setStyle(wd,Math.max(this.width - lw,0)+px);
            uft.setStyle(wd,uw+px);
            var lft = this.fb.child('table[stype=fb.lbt]');
            if(lft){
            var lf = this.fb.child('div[stype=grid.lf]');
                lf.setStyle(wd,(lw-1)+px);
                lft.setStyle(wd,lw+px);
            }
        }
        
        if(this.lc){
//            var lcw = Math.max(lw-1,0);
            var lcw = Math.max(lw,0);
            this.lc.setStyle(wd,lcw+px);
            this.lc.setStyle('display',lcw==0 ? 'none' : '');
        }
        if(this.lht)this.lht.setStyle(wd,lw+px);
        if(this.lbt)this.lbt.setStyle(wd,lw+px);
        this.uc.setStyle(wd, Math.max(this.width - lw,0)+px);
        this.uh.setStyle(wd,Math.max(this.width - lw,0)+px);
        this.ub.setStyle(wd,Math.max(this.width - lw,0)+px);
        this.uht.setStyle(wd,uw+px);
        if(this.ubt)this.ubt.setStyle(wd,uw+px);
        this.syncSize();
    },
    drawFootBar : function(objs){
    	if(!this.fb) return;
    	objs = [].concat((objs) ? objs : this.columns);
    	var sf = this;
    	Ext.each(objs, function(obj) {
    		var col = typeof(obj)==='string' ? sf.findColByName(obj) : obj;
            if(col&&col.footerrenderer){
                var name = col.name;
                var fder = $S.getFunction(col.footerrenderer);
                if(fder == null){
                    alert("未找到"+col.footerrenderer+"方法!")
                    return;
                }
                var v = fder.call(window,sf.dataset.datas, name);
                var t = sf.fb.child('td[dataindex='+name+']');
                t.update(v)
            }
    	});
    	
    },
    syncSize : function(){
        var lw = 0;
        for(var i=0,l=this.columns.length;i<l;i++){
            var c = this.columns[i];
            if(c.hidden !== true){
                if(c.lock === true){
                    lw += c.width;
                }
            }
        }
        if(lw !=0){
            var us = this.width -lw;
            this.uc.setWidth(us);
            this.uh.setWidth(us);
            this.ub.setWidth(us);
        }
    },
    /**
     * 显示某列.
     * 
     * @param {String} name 列的name
     */
    showColumn : function(name){
        var col = this.findColByName(name);
        if(col){
            if(col.hidden === true){
                delete col.hidden;
                this.setColumnSize(name, col.hiddenWidth);
                delete col.hiddenWidth;
//              if(!Ext.isIE){
                    var tds = Ext.DomQuery.select('td[dataindex='+name+']',this.wrap.dom);
                    for(var i=0,l=tds.length;i<l;i++){
                        var td = tds[i];
                        Ext.fly(td).show();
                    }
//              }
            }
        }   
    },
    /**
     * 隐藏某列.
     * 
     * @param {String} name 列的name;
     */
    hideColumn : function(name){
        var col = this.findColByName(name);
        if(col){
            if(col.hidden !== true){
                col.hiddenWidth = col.width;
                this.setColumnSize(name, 0, false);
//              if(!Ext.isIE){
                    var tds = Ext.DomQuery.select('td[dataindex='+name+']',this.wrap.dom);
                    for(var i=0,l=tds.length;i<l;i++){
                        var td = tds[i];
                        Ext.fly(td).hide();
                    }
//              }
                col.hidden = true;
            }
        }       
    },
    setWidth: function(w){
    	if(this.width == w) return;
        this.width = w;
        this.wrap.setWidth(w);
        var tb = $S.CmpManager.get(this.id+'_tb')
        if(tb)tb.setWidth(w);
        var nb = $S.CmpManager.get(this.id+'_navbar')
        if(nb)nb.setWidth(w);
        if(this.fb) this.fb.setWidth(w);
        var bw = w-this.lockWidth
        this.uc.setWidth(bw);
        if(this.uh)this.uh.setWidth(bw);
        this.ub.setWidth(bw);
        if(this.uf) this.uf.setWidth(bw);
    },
    setHeight: function(h){
    	if(this.height == h) return;
        this.height = h;
        var tb = $S.CmpManager.get(this.id+'_tb');
        if(tb)h-=this.ROW_HEIGHT;
        var nb = $S.CmpManager.get(this.id+'_navbar');
        if(nb)h-=this.ROW_HEIGHT;
        if(this.fb)h-=this.ROW_HEIGHT;
        this.wrap.setHeight(h);
        var bh = h - this.maxRow*this.ROW_HEIGHT;
        if(this.lb)this.lb.setHeight(bh)
        this.ub.setHeight(bh)
    },
    deleteSelectRows: function(win){
        var selected = [].concat(this.dataset.getSelected());
        if(selected.length >0){
            this.dataset.remove(selected);
        }
        win.close();  
    },
    remove: function(){
        var selected = this.dataset.getSelected();
        if(selected.length >0) {
            $S.showConfirm('确认','如果删除，这些数据将无法恢复。您确定吗?',this.deleteSelectRows.createDelegate(this),null, 350);    
        }else{
            $S.showInfoMessage('提示','没有选中任何数据，请重新选择!');
        }
    },
    destroy: function(){
        Ext.get(document.documentElement).un("mousedown", this.onEditorBlur, this);
        $S.Grid.superclass.destroy.call(this);
        this.processDataSetLiestener('un');
        this.sp.remove();
        delete this.sp;
    }
});
$S.Grid.revision='$Rev: 91 $';
/**
 * @class Aurora.Tab
 * @extends Aurora.Component
 * <p>Tab组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Tab = Ext.extend($S.ViewComponent,{
    sd:'scroll-disabled',
    tslo:'tab-scroll-left-over',
    tsro:'tab-scroll-right-over',
    tsl:'tab-scroll-left',
    tsr:'tab-scroll-right',
    tc:'tab-close',
    tbo:'tab-btn-over',
    tbd:'tab-btn-down',
    ts:'tab-scroll',
    scrollWidth:36,
	constructor: function(config){
        this.intervalIds=[];
		$S.Tab.superclass.constructor.call(this,config);
	},
	initComponent:function(config){
		$S.Tab.superclass.initComponent.call(this, config);
		this.scriptwidth = config.scriptwidth||70;
		this.scripts = this.wrap.child('div[stype=tab.strips]'); 
		this.body = this.wrap.child('div.s_tab_body');
		this.scrollLeft = this.wrap.child('div[stype=scroll-left]');
		this.scrollRight = this.wrap.child('div[stype=scroll-right]');
		this.script = this.scripts.parent();
		this.selectTab(config.selected||0)
	},
	processListener: function(ou){
    	$S.Tab.superclass.processListener.call(this,ou);
    	this.script.parent()[ou]('mousedown',this.onMouseDown, this);
    	this.script.parent()[ou]('mouseup',this.onMouseUp, this);
    	this.script.parent()[ou]('mouseover',this.onMouseOver, this);
    	this.script.parent()[ou]('mouseout',this.onMouseOut, this);
    	this.script[ou]('click',this.onClick, this);
    	this.script[ou]('mousewheel',this.onMouseWheel, this);
    },
	initEvents:function(){
		$S.Tab.superclass.initEvents.call(this);   
		this.addEvents(
		/**
         * @event select
         * 选择事件.
         * @param {Aurora.Tab} tab Tab对象.
         * @param {Number} index 序号.
         */
		'select',
        /**
         * @event beforeopen
         * 选择事件.
         * @param {Aurora.Tab} tab Tab对象.
         * @param {Number} index 序号. 
         */
		'beforeopen'
		);
		
	},
    /**
     * 根据id获取item 
     * @param {String} id
     * @return {Object}
     */
    getItemById : function(id){
        for(var i = 0;i<this.items.length;i++){
            var item = this.items[i];
            if(item.id==id){
                return item;
            }
        }
    },
	/**
	 * 选中某个Tab页
	 * @param {Number} index TabItem序号
	 */
	selectTab:function(index,callback){
		var tab=this.getTab(index);
		if(!tab)return;
        if(tab.strip.hasClass(this.sd)){
            this.selectTab(tab.index+1);
            return;
        }
		var activeStrip = tab.strip,activeBody = tab.body;
		index=tab.index;
		if(activeStrip){
			if(this.activeTab)this.activeTab.replaceClass('active','unactive');
			this.activeTab = activeStrip;
			activeStrip.replaceClass('unactive','active');
			var l=activeStrip.dom.offsetLeft,w=activeStrip.getWidth(),
				sl=this.script.getScroll().left,sw=this.script.getWidth(),hw=this.scripts.getWidth();
				tr=l+w-sl-sw,tl=sl-l;
			if(tr>0){
				this.scrollRight.removeClass(this.sd);
				this.scrollLeft.removeClass(this.sd);
				this.script.scrollTo('left',sl+tr);
			}else if(tl>0){
				this.scrollLeft.removeClass(this.sd);
				this.script.scrollTo('left',sl-tl);
				this.scrollRight.removeClass(this.sd);
			}
			if(sw+this.script.getScroll().left>=hw){
				this.script.scrollTo('left',hw-sw);
				this.scrollRight.addClass(this.sd);                
			}else if(index==0){
				this.script.scrollTo('left',0);
				this.scrollLeft.addClass(this.sd);
			}
		}
		if(activeBody){
			if(this.activeBody){
				this.activeBody.setLeft('-10000px');
				this.activeBody.setTop('-10000px');
			}
			this.activeBody = activeBody;
			activeBody.setLeft('0px');
			activeBody.setTop('0px');
		}
		if(this.items[index].ref && activeBody.loaded!= true){
			this.load(this.items[index].ref,activeBody,index,this.items[index].para,callback);
			activeBody.loaded = true;
		}else{
            if(callback) callback.call(this);
            this.fireEvent('select', this, index)
		}
	},
	stripTpl:['<div class="strip unactive"  unselectable="on" onselectstart="return false;">'
				,'<div class="strip-left"></div>',
				'<div style="width:{stripwidth}px;" class="strip-center"><div class="tab-close"></div>{prompt}</div>',
				'<div class="strip-right"></div>',
			'</div>'],
	bodyTpl:'<div style="width:{bodywidth}px;height:{bodyheight}px;left:-10000px;top:-10000px;" class="tab"></div>',
	/**
	 * 打开一个指定引用地址的Tab页，如果该指定的引用地址的页面已经被打开，则选中该Tab页
	 * @param {String} ref Tab页面的引用地址
	 * @param {String} title Tab的标题
	 */
	openTab : function(opt){
        var id = opt.id
        var ref = opt.ref;
        var prompt = opt.title;
		var i=0;
		for(;i<this.items.length;i++){
            var item = this.items[i];
			if(item.ref&&item.id==id){
				this.selectTab(i,opt.callback);
                return;
			}
		}
		if(this.fireEvent('beforeopen',this,i)){
			this.items.push({ref:ref,id:id});
			var stripwidth= $S.TextMetrics.measure(prompt).width + 35;
			stripwidth=stripwidth<this.scriptwidth?this.scriptwidth:stripwidth;
			var width=this.scripts.getWidth()+stripwidth+6;
			this.scripts.setWidth(width);
			if(this.scrollStatus!= true && width>this.width){                
                this.scrollStatus = true;
                this.script.setWidth(this.width-this.scrollWidth);
				this.scrollLeft.setStyle({'display':'block'});
				this.scrollRight.setStyle({'display':'block'});
			}
			new Ext.Template(this.stripTpl).append(this.scripts.dom,{'prompt':prompt,'stripwidth':stripwidth});//,'stripwidth2':stripwidth+6
			new Ext.Template(this.bodyTpl).append(this.body.dom,{'bodywidth':this.body.getWidth()-Ext.fly(this.body).getBorderWidth('lr'),'bodyheight':this.body.getHeight()-Ext.fly(this.body).getBorderWidth('tb')});
			this.selectTab(i,opt.callback);
		}
	},
	/**
	 * 关闭某个Tab页
	 * @param {Integer} index TabItem序号
	 */
	closeTab : function(o, checkDirty){
        
		var tab=this.getTab(o);
        
		if(!tab)return;
		var strip=tab.strip,body=tab.body,index=tab.index,cmps=body.cmps;
        var item = this.items[index];
        if(item.closeable == false) {
            $S.showWarningMessage('提示','当前标签页正在执行后台程序,无法关闭!');
            return;
        }
		if(!strip.child('div.' + this.tc)){
			$S.showWarningMessage('警告','该标签页无法被关闭!')
			return;
		}
        if(checkDirty != false){
            var pass = true;
            for(var key in cmps){
                var cmp = cmps[key];
                if(cmp instanceof $S.DataSet && cmp.checkDirty && cmp.isDirty()){
                    pass = false;
                    break;
                }
            }
            if(!pass){
                var sf = this;
                var cw = $S.showConfirm('提示','当前标签页存在未保存的数据,是否关闭?', function(){
                    sf.closeTab(o, false); 
                    cw.close();
                })
                return;
            }
        }
        
        
        if(this.activeBody == tab.body){
            this.activeBody=null;
            this.activeTab=null;
        }
		this.items.splice(index,1);
		var width=this.scripts.getWidth()-strip.getWidth();
		this.scripts.setWidth(width);//TODO:测试IE8下，多个tab页下关闭性能
		if(this.scrollStatus!=false && width <= this.width){
            this.scrollStatus = false;
            this.script.setWidth(this.width);
			this.scrollLeft.setStyle({'display':'none'});
			this.scrollRight.setStyle({'display':'none'});
		}
        
        for(var key in cmps){
            var cmp = cmps[key];
            if(cmp.destroy){
                try{
                    cmp.destroy();
                }catch(e){
                    alert('销毁标签页出错: ' + e)
                }
            }
        }
		strip.remove();
		body.remove();
		
		delete body.loaded;
//		setTimeout(function(){
//        	for(var key in cmps){
//        		var cmp = cmps[key];
//        		if(cmp.destroy){
//        			try{
//        				cmp.destroy();
//        			}catch(e){
//        				alert('销毁标签页出错: ' + e)
//        			}
//        		}
//        	}
//        },10)
        var i = index<(this.items.length-1) ? index : (this.items.length-1)
		this.selectTab(i);
	},
    destroy : function(){
        var bodys = this.body.dom.children;
        for(var i=0;i<bodys.length;i++){
            var body = Ext.get(bodys[i]),
            cmps=body.cmps;
            if(cmps){
                for(var key in cmps){
                    var cmp = cmps[key];
                    if(cmp.destroy){
                        try{
                            cmp.destroy();
                        }catch(e){
                            alert('销毁出错: ' + e)
                        }
                    }
                }
            }
        }
        $S.Tab.superclass.destroy.call(this); 
    },
    /**
     * 将某个Tab页设为不可用。当TabItem有且仅有1个时，该方法无效果。
     * @param {Integer} index TabItem序号。当index<0时，TabItem序号等于TabItem的个数加上index。
     */
    setDisabled : function(index){
        var tab = this.getTab(index);
        if(!tab)return;
        if(this.items.length > 1){
            if(this.activeTab==tab.strip){
                this.selectTab(tab.index+(this.getTab(tab.index+1)?1:-1))
            }
            tab.strip.addClass(this.sd);
        }
    },
    /**
     * 将某个Tab页设为可用
     * @param {Integer} index TabItem序号。当index<0时，TabItem序号等于TabItem的个数加上index。
     */
    setEnabled : function(index){
        var tab = this.getTab(index);
        if(!tab)return;
        tab.strip.removeClass(this.sd);
    },
	getTab : function(o){
		var bodys = this.body.dom.children,
        strips = this.scripts.dom.children,
        strip,body;
		if(Ext.isNumber(o)){
			if(o<0)o+=strips.length;
            o=Math.round(o);
            if(strips[o]){
                strip=Ext.get(strips[o]);
                body=Ext.get(bodys[o]);
            }
		}else if(Ext.isString(o)){
            var item = this.getItemById(o);
            var index = this.items.indexOf(item);
            return this.getTab(index);
        } else {
			o = Ext.get(o);
			for(var i=0,l= strips.length;i<l;i++){
				if(Ext.get(strips[i]) == o){
					strip=o;
					body=Ext.get(bodys[i]);
					o=i;
					break;
				}
			}
		}
		return strip?{'strip':strip,'body':body,'index':o}:null;
	},
	scrollTo : function(lr){
		if(lr=='left'){
			this.script.scrollTo('left',this.script.getScroll().left-this.scriptwidth);
			this.scrollRight.removeClass(this.sd);
			if(this.script.getScroll().left<=0){
				this.scrollLeft.addClass(this.sd);
				this.scrollLeft.replaceClass(this.tslo,this.tsl);
				this.stopScroll();
			}
		}else if(lr=='right'){
			this.script.scrollTo('left',this.script.getScroll().left+this.scriptwidth);
			this.scrollLeft.removeClass(this.sd);
			if(this.script.getScroll().left+this.script.getWidth()>=this.scripts.getWidth()){
				this.scrollRight.addClass(this.sd);
				this.scrollRight.replaceClass(this.tsro,this.tsr);
				this.stopScroll();
			}
		}
	},
	stopScroll : function(){
		if(this.scrollInterval){
			clearInterval(this.scrollInterval);
			delete this.scrollInterval;
		}
	},
	onClick : function(e){
		var el=Ext.get(e.target);
		if(el.hasClass(this.tc))this.closeTab(el.parent('.strip'));
	},
	onMouseWheel : function(e){
		var delta = e.getWheelDelta();
        if(delta > 0){
            this.scrollTo('left');
            e.stopEvent();
        }else{
            this.scrollTo('right');
            e.stopEvent();
        }
	},
	onMouseDown : function(e){
		var el=Ext.get(e.target),strip = el.parent(),sf=this;
        if(el.hasClass(sf.tc)){
            el.removeClass(sf.tbo);
            el.addClass(sf.tbd);
        }else if(el.hasClass(sf.ts) && !el.hasClass(sf.sd)){
            if(el.hasClass(sf.tslo))sf.scrollTo('left');
            else sf.scrollTo('right');
            sf.scrollInterval=setInterval(function(){
                if(el.hasClass(sf.ts)&&!el.hasClass(sf.sd)){
                    if(el.hasClass(sf.tslo))sf.scrollTo('left');
                    else sf.scrollTo('right');
                    if(el.hasClass(sf.sd))clearInterval(sf.scrollInterval);
                }
            },100);
        }else if(strip.hasClass('strip') && !strip.hasClass('active') && !strip.hasClass(sf.sd)){
            sf.selectTab(strip);
        }
	},
	onMouseUp : function(e){
		this.stopScroll();
	},
	onMouseOver : function(e){
        var el=Ext.get(e.target),strip = el.parent('.strip');
        if(el.hasClass(this.ts)&&!el.hasClass(this.sd)){
            if(el.hasClass(this.tsl))el.replaceClass(this.tsl,this.tslo);
            else if(el.hasClass(this.tsr))el.replaceClass(this.tsr,this.tsro);
        } else if(el.hasClass(this.tc)){
            el.addClass(this.tbo);
        }
        if(strip){
            var el = strip.child('div.'+this.tc);
            if(el){
                if(this.currentBtn)this.currentBtn.hide();
                this.currentBtn=el;
                el.show();
            }            
        }
    },
    onMouseOut : function(e){
        var el=Ext.get(e.target),strip = el.parent('.strip');
        if(el.hasClass(this.ts)&&!el.hasClass(this.sd)){
            this.stopScroll();
            if(el.hasClass(this.tslo))el.replaceClass(this.tslo,this.tsl);
            else if((el.hasClass(this.tsro)))el.replaceClass(this.tsro,this.tsr);
        }else if(el.hasClass(this.tc)){
            el.removeClass(this.tbo);
            el.removeClass(this.tbd);
        }
        if(strip){
            el = strip.child('div.'+this.tc);
            if(el){
                el.hide();
            }            
        }
    },
	showLoading : function(dom){
    	dom.update('正在加载...');
    	dom.setStyle('text-align','center');
    	dom.setStyle('line-height',5);
    },
    clearLoading : function(dom){
    	dom.update('');
    	dom.setStyle('text-align','');
    	dom.setStyle('line-height','');
    },
	load : function(url,dom,index,para,callback){
        var sf = this,body = Ext.get(dom);
        body.cmps={};
        sf.showLoading(body);
        //TODO:错误信息
        if(para) {
            url = url + '?' + Ext.urlEncode(para);
        }
        Ext.Ajax.request({
            url: url,
            method: 'POST',
            params:{SUK:$S.suk,_sph:$S.viewHeight,_spw:$S.viewWidth},
            success: function(response, options){
                var html = response.responseText;
                sf.clearLoading(body);
                body.update(html,true,function(){
                    if(callback) callback.call(this)
                    sf.fireEvent('select', sf, index)
                },body);
            }
        });
    },
    setWidth : function(w){
    	w = Math.max(w,2);
    	if(this.width==w)return;
    	$S.Tab.superclass.setWidth.call(this, w);
        this.body.setWidth(w);
        this.script.setWidth(w-38);
    	if(w-38<this.scripts.getWidth()){
			this.scrollLeft.setStyle({'display':'block'});
			this.scrollRight.setStyle({'display':'block'});
			var sl=this.script.getScroll().left,sw=this.script.getWidth(),hw=this.scripts.getWidth();
			if(sl<=0) {
                this.scrollLeft.addClass(this.sd);
            } else {
                this.scrollLeft.removeClass(this.sd);
            }
			if(sl+sw>=hw){
				(!this.scrollRight.hasClass(this.sd)) ? this.scrollRight.addClass(this.sd) : this.script.scrollTo('left',hw-sw);
			}else {
                this.scrollRight.removeClass(this.sd);
            }
    	}else{
			this.scrollLeft.setStyle({'display':'none'});
			this.scrollRight.setStyle({'display':'none'});
			this.script.scrollTo('left',0);
    	}
        var bodys = this.body.dom.children;
        for(var i=0;i<bodys.length;i++){
            var body = bodys[i];
            Ext.fly(body).setWidth(w);//-2
        }
    },
    setHeight : function(h){
    	h = Math.max(h,25);
    	if(this.height==h)return;
    	$S.Tab.superclass.setHeight.call(this, h);
        this.body.setHeight(h-(27-Ext.fly(this.body).getBorderWidth('tb')));
        var bodys = this.body.dom.children;
        for(var i=0;i<bodys.length;i++){
            var body = bodys[i];
            Ext.fly(body).setHeight(h-27);
        }
    }
});
/**
 * @class Spark.Tree
 * @extends Spark.Component
 * <p>树形组件.
 * @author njq.niu@hand-china.com
 * @constructor
 * @param {Object} config 配置对象. 
 */
$S.Tree = Ext.extend($S.ViewComponent,{
    showSkeleton: true,
    sw:18,
    constructor: function(config){
        $S.Tree.superclass.constructor.call(this,config);
        this.context = config.context||'';
    },
    initComponent:function(config){
        this.nodeHash = {};
        $S.Tree.superclass.initComponent.call(this, config);
        this.body = this.wrap.child('div[stype=tree.body]');
    },
    initBind : function(){
        if(this.bt) this.bind(this.bt)
    },
    processListener: function(ou){
        $S.Tree.superclass.processListener.call(this,ou);
        this.wrap[ou]('click', this.onClick, this)
                 [ou]('dblclick',this.onDblclick, this);
    },
    initEvents:function(){
        $S.Tree.superclass.initEvents.call(this);
        this.addEvents(
        /**
         * @event render
         * tree渲染事件.
         */
        'render',
        /**
         * @event collapse
         * 节点折叠事件.
         */
        'collapse',
        /**
         * @event expand
         * 节点展开事件.
         */
        'expand',
        /**
         * @event click
         * 点击事件.
         * @param {Spark.Record} record 选中的Record对象
         * @param {Spark.Tree.TreeNode} node 节点对象
         */
        'click',
        /**
         * @event dblclick
         * 鼠标双击事件.
         * @param {Spark.Tree} tree tree组件
         * @param {Spark.Record} record 当前record.
         * @param {Spark.Tree.TreeNode} node 节点对象
         */
        'dblclick');
    },
    destroy : function(){
        $S.Tree.superclass.destroy.call(this);
    },
    processDataSetLiestener: function(ou){
        var ds = this.dataset;
        if(ds){
            ds[ou]('update', this.onUpdate, this);
            ds[ou]('load', this.onLoad, this);
            ds[ou]('indexchange', this.onIndexChange, this);
        }
    },
    bind: function(ds){
        if(typeof(ds)==='string'){
            ds = $(ds);
            if(!ds) return;
        }
        var sf = this;
        sf.dataset = ds;
        sf.processDataSetLiestener('on');
        if(ds.datas.length >0)
        Ext.onReady(function(){
            sf.onLoad();
        })
    },
    onUpdate : function(ds, record, name, value){
        if(this.parentField == name || name == this.sequenceField){
            this.onLoad();
        }else{
            var node = this.nodeHash[record.id];
            if(node)node.paintText();
        }
    },
    onIndexChange:function(ds, record){
        var node = this.nodeHash[record.id];
        if(node)this.setFocusNode(node);
    },
    onDblclick : function(e,t){
        var elem = Ext.fly(t).findParent('td');
        if(!elem)return;
        var _type = elem['_type_'];
        if(typeof(_type) === undefined){
            return;
        }
        if(_type == 'icon' || _type == 'text'){
            elem = Ext.fly(e.target).findParent('div.s_node');
            if(!elem)return;
            var node = this.nodeHash[elem.indexId];
//            if(node && node.childNodes.length){
//                if(node.isExpand){
//                    node.collapse();
//                    this.fireEvent('collapse', this, node);
//                }else{
//                    node.expand();
//                    this.fireEvent('expand', this, node);
//                }
//            }
            var ds = this.dataset,r = node.record;
            ds.locate(ds.indexOf(r)+1,true);
            this.fireEvent('dblclick', this, r, node);
        }
    },
    onClick : function(e){
        e.stopEvent();
        var elem = Ext.fly(e.target).findParent('td');
        if(!elem)return;
        var _type = elem['_type_'];
        if(typeof(_type) === undefined){
            return;
        }
        elem = Ext.fly(e.target).findParent('div.s_node');
        if(!elem)return;
        var node = this.nodeHash[elem.indexId ];
        if(_type == 'clip'){
            if(elem.indexId!=null){             
                if(node.isExpand){
                    node.collapse();
                    this.fireEvent('collapse', this, node);
                }else{
                    node.expand();
                    this.fireEvent('expand', this, node);
                }
            }
        }else if(_type == 'icon' || _type == 'text'){
            this.setFocusNode(node);
            this.dataset.locate(this.dataset.indexOf(node.record)+1,true);
            this.fireEvent('click', this, node.record, node);
        }else if(_type == 'checked'){
            node.onCheck();
        }
    },
    getRootNode : function(){
        return this.root;
    },
    setRootNode : function(node){
        this.root = node;
        node.ownerTree = this;
        this.registerNode(node);
        node.cascade((function(node){
            this.registerNode(node);
        }),this);
    },
    /**
     * 根据id获取节点对象
     * @param {Number} id 节点id
     * @return {Spark.Tree.TreeNode} node 节点对象
     */
    getNodeById : function(id){
        return this.nodeHash[id];
    },
    registerNode : function(node){
        this.nodeHash[node.id] = node;
    },
    unregisterNode : function(node){
        delete this.nodeHash[node.id];
    },
    /**
     * 设置节点焦点
     * @param {Spark.Tree.TreeNode} node 节点对象.
     */
    setFocusNode : function(node){
        if(this.focusNode){
            this.focusNode.unselect();
        }
        this.focusNode = node;
        if(node.parentNode){
            node.parentNode.expand();
        }
        node.select();
    },
    createNode: function(record){
        return {
            record:record,
            children:[]
        }
    },
    buildTree: function(){
        var array = [];
        var map1 = {};
        var map2 = {};
        var datas = this.dataset.datas;
        var l = datas.length;
        for(var i=0;i<l;i++){
            var record = datas[i];
            var id = record.get(this.idField);
            var node = this.createNode(record);
            node.checked = (node.record.get(this.checkField) == "Y") ? 1 : 0;            
            node.expanded = !Ext.isEmpty(this.expandAll) ? this.expandAll :  node.record.get(this.expandField) == "Y";
            map1[id] = node;
            map2[id] = node;
        }
        for(var key in map2){
            var node = map2[key];
            var record = node.record;
            var pid = record.get(this.parentField);
            var parent = map2[pid];
            if(parent){
                parent.children.add(node);
                delete map1[key];
            }
        }
        for(var key in map1){
            var node = map2[key];
            array.add(node);
        }
        var rtnode = null;
        if(array.length == 1){
            this.showRoot = true;
            rtnode = array[0];
        }else{
            var data = {};
            data[this.displayField] = '_root';
            var record =  new Spark.Record(data);
            record.ds = this.dataset;
            var root = { 
                'record':record,
                'children':[]
            }
            for(var i=0;i<array.length;i++){
                root['children'].add(array[i]);
            }
            this.showRoot = false;
            rtnode = root;
        }
        
        var process = function(item){
            var children = item.children;
            var count = children.length;
            for(var i=0;i<count;i++){
                var node = children[i]
                if(node.children.length >0){
                    process(node);
                } 
            }                     
            
            var checked1 = 0;
            for(var i=0;i<count;i++){
                var checked = children[i].checked;
                if(checked==1){
                    checked1++;
                }
            }
            if(checked1==0){
                item.checked = 0;
            }else if(count==checked1){
                item.checked = 1;
            }else {
                item.checked = 2;
            }
        }
        
        for(var i=0;i<array.length;i++){
            var item = array[i];
            process(item)
        }
        
        
        this.sortChildren(rtnode.children,this.sequenceField);
        return rtnode;
    },
    sortChildren : function(children,sequence){
        if(sequence){
            var m = Number.MAX_VALUE;
            children.sort(function(a, b){
                return parseFloat(a.record.get(sequence)||m) - parseFloat(b.record.get(sequence)||m);
            });
        }else{
            children.sort();
        }
        Ext.each(children,function(n){
            this.sortChildren(n.children,sequence)
        },this);
    },
    createTreeNode : function(item){
        return new $S.Tree.TreeNode(item);
    },
    onLoad : function(){
        var root = this.buildTree();
        if(!root) {
            return;
        }
        
        var node = this.createTreeNode(root);
        this.setRootNode(node); 
        this.body.update('');
        if(this.dataset.datas.length>0)
        this.root.render();
        this.fireEvent('render', this,root);
    },
    getIconByType : function(type){
        return type;
    },
    onNodeSelect : function(el){
        Ext.fly(el[this.displayField+'_td']).addClass('node-select');
//        el[this.displayField+'_td'].style.backgroundColor='#ffffd5';
    },
    onNodeUnSelect : function(el){
       Ext.fly(el[this.displayField+'_td']).removeClass('node-select');
//       el[this.displayField+'_td'].style.backgroundColor='';
    }
})
/**
 * @class Spark.Tree.TreeNode
 * @extends Spark.Component
 * <p>树节点对象.
 * @author njq.niu@hand-china.com
 * @constructor 
 */
$S.Tree.TreeNode = function(data) {
    this.init(data)
}
$S.Tree.TreeNode.prototype={
    init : function(data){
        this.data = data;
        this.record = data.record;
        this.els = null;
        this.id = this.record.id;
        this.parentNode = null;
        this.childNodes = [];
        this.lastChild = null;
        this.firstChild = null;
        this.previousSibling = null;
        this.nextSibling = null;
        this.childrenRendered = false;
        this.isExpand = data.expanded;//false;    
        this.checked = data.checked;
        Ext.each(data.children,function(node){
            this.appendChild(this.createNode(node));
        },this);
    },
    createNode : function(item){
        return new $S.Tree.TreeNode(item);
    },
    createCellEl : function(df){     
    },
    initEl : function(){
        var tree = this.getOwnerTree(),df = tree.displayField,text = this.record.get(df),div = document.createElement('div'),
            child = document.createElement('div'),table = document.createElement('table'),tbody = document.createElement('tbody'),
            tr = document.createElement('tr'),checkbox;
        div.className = 's_node';
        table.border=0;
        table.cellSpacing=0;
        table.cellPadding=0;
        this.els = {element:div,itemNodeTable:table,itemNodeTbody:tbody,itemNodeTr:tr,child:child};
        tbody.appendChild(tr);
        table.appendChild(tbody);
        div.appendChild(table);
        if(tree.showSkeleton){
            var line = tr.insertCell(-1),clip = tr.insertCell(-1),icon = this.icon? document.createElement('img') : null,iconTd = tr.insertCell(-1);
            checkbox = tr.insertCell(-1);
            line['_type_'] ='line';
            line.className ='line';
            clip['_type_'] ='clip';
            clip.innerHTML = '&#160';
            iconTd['_type_'] ='icon';
            checkbox['_type_'] ='checked';
            checkbox.innerHTML = '&#160';
            iconTd.className='node-icon';
            if(icon) {
                iconTd.appendChild(icon);
            }else {
                iconTd.innerHTML = '&#160';
            }
            Ext.apply(this.els,{line:line,clip:clip,icon:icon,iconTd:iconTd,checkbox:checkbox})
            var td = tr.insertCell(-1);
            td['_type_'] ='text';
            this.els[df + '_td'] = td;
            this.createCellEl(df);
            td.className='node-text'
        }
        
        div.noWrap='true';
        
        if(tree.showCheckBox === false && checkbox) {
            checkbox.style.display='none';
        }
        if(this.isRoot() && text=='_root'){
            table.style.display='none';
        }
        div.appendChild(child);
        child.className= 's_child';
        child.style.display='none';
        
    },
    render : function(){
        var tree = this.getOwnerTree();
        this.icon = this.record.get(tree.iconField);
        if(!this.els){
            this.initEl();
        }
        if(this.isRoot()){
            tree.body.appendChild(this.els['element']);
            if(this.getOwnerTree().showRoot == false && this.getOwnerTree().showSkeleton)
            this.els['iconTd'].style.display=this.els['checkbox'].style.display=this.els[tree.displayField+'_td'].style.display='none';
            this.expand();
        }else{
            this.parentNode.els['child'].appendChild(this.els['element']);
            if(this.isExpand)
            this.expand();
        }
        this.initColumns();
        this.paintPrefix();
        this.els['element'].indexId = this.id;
        this.paintCheckboxImg();
    },
    initColumns : function(){},
    paintPrefix : function(){
        if(!this.getOwnerTree().showSkeleton) return;
        this.paintLine();
        this.paintClipIcoImg();
        this.paintCheckboxImg();
        this.paintIconImg();
        this.paintText();
    },
    paintLine : function(){
        var ownerTree = this.getOwnerTree();
        var pathNodes = this.getPathNodes(),
            w = (pathNodes.length-2)*ownerTree.sw,
            line = this.els['line'];
        line.innerHTML = '';
        Ext.fly(line).setStyle('width',Math.max(w,0)+'px');
        if(w==0){
            line.style.display='none';
        }
        for(var i = 1 ,count = pathNodes.length-1 ; i < count ; i++){
            var node = pathNodes[i],
                ld = document.createElement('div');
            ld.className = node.isLast()?'node-empty':'node-line';
            //Ext.fly(ld).setWidth(ownerTree.sw);
            line.appendChild(ld);
        }
    },
    paintClipIcoImg : function(){
        var ownerTree = this.getOwnerTree(),icon = 'empty',clip = this.els['clip'];
        if(!ownerTree.showSkeleton) return;
        if(this.isRoot()){
            clip.style.display='none';//不显示根节点的clip
            return;
        } else {
            if(this.isLeaf()){
                if(this.isLast()){
                    icon = 'joinBottom';
                } else if(this.isFirst()){
                    icon = 'joinTop';
                }else{
                    icon = 'join';
                }
            }else{
                if(this.isExpand){
                    if(this.isLast()){
                        icon = 'minusBottom';
                    } else if(this.isFirst()){
                        icon = 'minusTop';
                    }else{
                        icon = 'minus';
                    }
                }else{
                    if(this.isLast()){
                        icon = 'plusBottom';
                    } else if(this.isFirst()){
                        icon = 'plusTop';
                    }else{
                        icon = 'plus';
                    }
                }
            }
        };
        clip.className = 'node-clip clip-' + icon;
    },
    paintIconImg : function(){
        var ownerTree = this.getOwnerTree();
        if(!ownerTree.showSkeleton) return;
        var data = this.data,icon = data.icon,eicon = this.els['icon'],iconTd = this.els['iconTd'];
        if(!icon){
            var type = data.type;
            if(type){
                icon = ownerTree.getIconByType(type);
            }
            if(!icon){
                if(this.isRoot()){
                    icon = 'root';
                }else if(this.isLeaf()){
                    icon = 'node';
                }else if(this.isExpand){
                    icon = 'folderOpen';
                }else{
                    icon = 'folder';
                }
            }
        }
        if(this.icon) {
            eicon.className = 'node-icon';
            eicon.src = ownerTree.context + this.icon;
        }else{
            iconTd.className = 'node-icon icon-' + icon;
        }
    },
    paintCheckboxImg : function(){
        var ownerTree = this.getOwnerTree();    
        if(!ownerTree.showSkeleton) return;
        var checked = this.checked;
        if(this.els){
            this.els['checkbox'].className = ((checked==2)?'checkbox2':(checked==1)?'checkbox1':'checkbox0');
        }
    },  
    paintText : function(){
        if(!this.els) return;
        var tree = this.getOwnerTree();
        var text = this.record.get(tree.displayField)
        if(!Ext.isEmpty(tree.renderer)){
            var renderer = window[tree.renderer];
            if(renderer)
            text = renderer.call(this, text, this.record, this);
        }
        this.syncWidth(tree,text);
        var tel =this.els[tree.displayField+'_td'];
        if(tel && text)tel.innerHTML=text;
        
    },
    syncWidth : function(tree,text){
        var w = ((tree.showCheckBox ? 1 : 0) +this.getPathNodes().length)*tree.sw;
        w = w+$S.TextMetrics.measure(text).width + 6;//padding-left(2)+padding-right(2)+border-left(1)+border-right(1)
        Ext.fly(this.els['itemNodeTable']).setWidth(w);
    },
    
    paintChildren : function(){
        if(!this.childrenRendered){
//          this.els['child'].innerHTML = '';
            this.childrenRendered = true;
            Ext.each(this.childNodes,function(node){
                node.render();
            });
        };
    },
    /**
     * 折叠收起
     */
    collapse : function(){
        this.isExpand=false;
        if(!this.isRoot())
        this.record.set(this.getOwnerTree().expandField,"N");
        this.els['child'].style.display='none';
        this.paintIconImg();
        this.paintClipIcoImg();
        this.refreshDom();
    },
    /**
     * 展开
     */
    expand : function(){
        if(!this.isLeaf()&&this.childNodes.length>0){
            if(!this.isRoot())
            this.record.set(this.getOwnerTree().expandField,"Y");
            this.isExpand=true;
            this.paintChildren();
            this.els['child'].style.display='block';
        }
        this.paintIconImg();
        this.paintClipIcoImg();
        this.refreshDom();
    },
    refreshDom : function(){
        this.getOwnerTree().wrap.addClass('a').removeClass('a');
    },
    /**
     * 选中节点
     */
    select : function(){
        this.isSelect = true;
        this.getOwnerTree().onNodeSelect(this.els);
    },
    /**
     * 取消选择
     */
    unselect : function(){
        this.isSelect = false;
        this.getOwnerTree().onNodeUnSelect(this.els);
    },
    getEl :  function(){
        return this.els;
    },
    setCheckStatus : function(checked){
        var c;
        if(checked==2||checked==3){
            var childNodes = this.childNodes,count = childNodes.length;
            if(count==0){
                c=checked==2?0:1;
            }else{
                var checked1 = 0,checked2 = 0;
                for(var i=0;i<count;i++){
                    var checked = childNodes[i].checked;
                    if(checked==1){
                        checked1++;
                    }else if(checked==2){
                        checked2++;
                    }
                }
                c = (childNodes.length==checked1) ? 1 : (checked1>0||checked2>0) ? 2 : 0;
            }
        }else{
            c=checked;
        }
        this.checked = c;
        if(!this.isRoot()) {
            this.record.set(this.getOwnerTree().checkField, (c==1||c==2) ? "Y" : "N");
        }
        this.paintCheckboxImg();
    },
    setCheck : function(cked){
        var a = cked?1:0,b=a+2;
        this.cascade(function(node){
            node.setCheckStatus(a);
        });
        this.bubble(function(node){
            node.setCheckStatus(b);
        });
    },
    onCheck : function(){
        this.setCheck(this.checked == 0);
    },
    /**
     * 是否是根节点
     * @return {Boolean} isroot 是否根节点.
     */
    isRoot : function(){
        return this.ownerTree && this.ownerTree.root === this;
    },
    /**
     * 是否叶子节点
     * @return {Boolean} isleaf 是否叶子节点.
     */
    isLeaf : function(){
        return this.childNodes.length===0;
    },
    /**
     * 是否是最后一个节点.
     * @return {Boolean} islast 是否是最后.
     */
    isLast : function(){
        return (!this.parentNode ? true : this.parentNode.childNodes[this.parentNode.childNodes.length-1] == this);
    },
    /**
     * 是否是第一个
     * @return {Boolean} isfirst 是否是第一个.
     */
    isFirst : function(){
        var tree = this.getOwnerTree();
        return (this.parentNode== tree.getRootNode()&&!tree.showRoot&&(this.parentNode.childNodes[0] == this));
    },
    hasChildNodes : function(){
        return !this.isLeaf() && this.childNodes.length > 0;
    },
    setFirstChild : function(node){
        this.firstChild = node;
    },
    setLastChild : function(node){
        this.lastChild = node;
    },
    appendChild : function(node){
        var multi = false;
        if(node instanceof Array){
            multi = node;
        }else if(arguments.length > 1){
            multi = arguments;
        }
        if(multi){
        for(var i = 0, len = multi.length; i < len; i++) {
                this.appendChild(multi[i]);
            }
        }else{
            //>>beforeappend
            var oldParent = node.parentNode;
            //>>beforemove
            if(oldParent){
                oldParent.removeChild(node);
            }
            var index = this.childNodes.length;
            if(index == 0){
                this.setFirstChild(node);
            }
            this.childNodes.push(node);
            node.parentNode = this;
            //
            var ps = this.childNodes[index-1];
            if(ps){
                node.previousSibling = ps;
                ps.nextSibling = node;
            }else{
                node.previousSibling = null;
            }
            node.nextSibling = null;
            this.setLastChild(node);
            node.setOwnerTree(this.getOwnerTree());
            //>>append
            //if(oldParent) >>move

            if(node && this.childrenRendered){
                node.render();
                if(node.previousSibling){
                    node.previousSibling.paintPrefix();//paintLine();
                }
            }
            if(this.els){
                this.paintPrefix();
            }
            return node;//true
        }
    },
    removeChild : function(node){
        var index = this.childNodes.indexOf(node);
        if(index == -1){
            return false;
        }
        //>>beforeremove
        this.childNodes.splice(index, 1);
        if(node.previousSibling){
            node.previousSibling.nextSibling = node.nextSibling;
        }
        if(node.nextSibling){
            node.nextSibling.previousSibling = node.previousSibling;
        }
        if(this.firstChild == node){
            this.setFirstChild(node.nextSibling);
        }
        if(this.lastChild == node){
            this.setLastChild(node.previousSibling);
        }
        node.setOwnerTree(null);
        //clear
        node.parentNode = null;
        node.previousSibling = null;
        node.nextSibling = null;
        //>>remove UI
        if(this.childrenRendered){
            if(node.els&&node.els['element']){
                this.els['child'].removeChild(node.els['element'])  
            }
            if(this.childNodes.length==0){
                this.collapse();
            }
        }
        if(this.els){
            this.paintPrefix();
        }
        return node;
    },
    insertBefore : function(node, refNode){
        if(!refNode){
            return this.appendChild(node);
        }
        //移动位置是自身位置(不需要移动)
        if(node == refNode){
            return false;
        }
        var index = this.childNodes.indexOf(refNode);
        var oldParent = node.parentNode;
        var refIndex = index;
        //是子节点，并且是向后移动
        if(oldParent == this && this.childNodes.indexOf(node) < index){
            refIndex--;
        }
        if(oldParent){
            oldParent.removeChild(node);
        }
        //设置节点间关系
        if(refIndex == 0){
            this.setFirstChild(node);
        }
        this.childNodes.splice(refIndex, 0, node);
        node.parentNode = this;
        var ps = this.childNodes[refIndex-1];
        if(ps){
            node.previousSibling = ps;
            ps.nextSibling = node;
        }else{
            node.previousSibling = null;
        }
        node.nextSibling = refNode;
        refNode.previousSibling = node;
        node.setOwnerTree(this.getOwnerTree());
        return node;
    },
    replaceChild : function(newChild, oldChild){
        this.insertBefore(newChild, oldChild);
        this.removeChild(oldChild);
        return oldChild;
    },
    indexOf : function(child){
        return this.childNodes.indexOf(child);
    },
    getOwnerTree : function(){
        if(!this.ownerTree){
            var p = this;
            while(p){
                if(p.ownerTree){
                    this.ownerTree = p.ownerTree;
                    break;
                }
                p = p.parentNode;
            }
        }
        return this.ownerTree;
    },
    setOwnerTree : function(tree){
        if(tree != this.ownerTree){
            if(this.ownerTree){
                this.ownerTree.unregisterNode(this);
            }
            this.ownerTree = tree;
            var cs = this.childNodes;
            for(var i = 0, len = cs.length; i < len; i++) {
                cs[i].setOwnerTree(tree);
            }
            if(tree){
                tree.registerNode(this);
            }
        }
    },
    getPathNodes : function(){
        var nodes = [];
        for(var parent=this; parent!=null; parent=parent.parentNode){nodes.push(parent);};
        return nodes.reverse();
    },
    bubble : function(fn, scope, args){
        var p = this;
        while(p){
            if(fn.call(scope || p, args || p) === false){
                break;
            }
            p = p.parentNode;
        }
    },
    cascade : function(fn, scope, args){
        if(fn.call(scope || this, args || this) !== false){
            var cs = this.childNodes;
            for(var i = 0, len = cs.length; i < len; i++) {
                cs[i].cascade(fn, scope, args);
            }
        }
    },
    findChild : function(attribute, value){
        var cs = this.childNodes;
        for(var i = 0, len = cs.length; i < len; i++) {
            if(cs[i].attributes[attribute] == value){
                return cs[i];
            }
        }
        return null;
    },
    findChildBy : function(fn, scope){
        var cs = this.childNodes;
        for(var i = 0, len = cs.length; i < len; i++) {
            if(fn.call(scope||cs[i], cs[i]) === true){
                return cs[i];
            }
        }
        return null;
    },
    sort : function(fn, scope){
        var cs = this.childNodes;
        var len = cs.length;
        if(len > 0){
            var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
            cs.sort(sortFn);
            for(var i = 0; i < len; i++){
                var n = cs[i];
                n.previousSibling = cs[i-1];
                n.nextSibling = cs[i+1];
                if(i == 0){
                    this.setFirstChild(n);
                }
                if(i == len-1){
                    this.setLastChild(n);
                }
            }
        }
    }
};
$S.TreeGrid = Ext.extend($S.Grid, {    
    initComponent : function(config) {
        $S.TreeGrid.superclass.initComponent.call(this, config);
        delete config.marginHeight;
        delete config.marginWidth;
        if (this.lockColumns.length > 0) {
            var ltid = this.id + "_lb_tree",
                ltc = this.createTreeConfig(config, this.lockColumns, ltid, true,this);
            this.lb.set({id : ltid});//.addClass('item-treegrid');
            var lockTree = this.lockTree = new $S.Tree(ltc);
            lockTree.body = this.lb;
            lockTree.treeGrid = this;
            lockTree.on('render', function() {
                    this.processData();
                    Ext.DomHelper.insertHtml("beforeEnd", this.lb.dom,'<div style="height:17px"></div>');
                }, this)
            lockTree.on('expand', function(tree, node) {
                this.unlockTree.getNodeById(node.id).expand();
            }, this);
            lockTree.on('dblclick',function(tree, r){
                this.fireEvent('dblclick', this, r)           
            },this);
            lockTree.on('collapse', function(tree, node) {
                this.unlockTree.getNodeById(node.id).collapse();
            }, this);
        }
        var utid = this.id + "_ub_tree",
            tc = this.createTreeConfig(config, this.unlockColumns, utid,this.lockColumns.length == 0, this);
        this.ub.set({id : utid}).addClass('item-treegrid');
        var unlockTree = this.unlockTree = new $S.Tree(tc);
        unlockTree.body = this.ub;
        unlockTree.treeGrid = this;
        unlockTree.on('dblclick',function(tree, r){
            this.fireEvent('dblclick', this, r)
        },this);
        unlockTree.on('render', this.processData, this);
    },
    onFocusKeyDown : function(){},
    initTemplate : function() {
        $S.TreeGrid.superclass.initTemplate.call(this);
//        this.cbTpl = new Ext.Template('<center style="width:{width}px"><div class="{cellcls}" style="height:13px;padding:0px;" id="'
//                + this.id + '_{name}_{recordid}"></div></center>');
    },
    createTemplateData : function(col, record) {
        return {
            width : col.width - 2,
            // cwidth:col.width-4,
            recordid : record.id,
            visibility : col.hidden === true ? 'hidden' : 'visible',
            name : col.name
        }
    },
    createTreeConfig : function(config, columns, id, showSkeleton, grid) {
        var dc = columns[0];
        return {
            sw : 18,
            id : id,
            expandAll:config.expandAll,
            checkField:config.checkField,
            displayField:config.displayField,
            expandField:config.expandField,
            parentField:config.parentField,
            sequenceField:config.sequenceField,
            showCheckBox:config.showCheckBox,
            iconField:config.iconField,
            idField:config.idField,
            showSkeleton : showSkeleton,
            columns : columns,
            displayField : dc.name,
            grid: grid,
            renderer : dc.renderer,
            createTreeNode : function(item) {
                return new $S.Tree.TreeGridNode(item);
            },
            onNodeSelect : function(el) {
                el['itemNodeTable'].style.backgroundColor = '#ffffd5';
            },
            onNodeUnSelect : function(el) {
                el['itemNodeTable'].style.backgroundColor = '';
            }
        }
    },
    processData : function(tree, root) {
        if (!root)
            return;
        var sf = this,items = [];
        if (tree.showRoot) {
            sf.processNode(items, root)
        } else {
            Ext.each(root.children,function(child){
                sf.processNode(items, child);
            });
        }
        sf.dataset.data = items;
        sf.editing &&
            sf.positionEditor();
        
    },
    onLoad : function(){
        this.drawFootBar();
        $S.Masker.unmask(this.wb);
    },
    processNode : function(items, node) {
        items.add(node.record);
        Ext.each(node.children,function(child){
            this.processNode(items, child);
        },this);
    },
    bind : function(ds) {
        if (typeof(ds) === 'string') {
            ds = $S.CmpManager.get(ds);
            if (!ds)
                return;
        }
        var sf = this;
        sf.dataset = ds;
        sf.processDataSetLiestener('on');
        if (sf.lockTree)
            sf.lockTree.bind(ds);
        sf.unlockTree.bind(ds);
        sf.drawFootBar();
    },
    setColumnSize : function(name, size) {
        $S.TreeGrid.superclass.setColumnSize.call(this, name, size);
        var sf = this,c = sf.findColByName(name),
            tree = c.lock == true ? sf.lockTree : sf.unlockTree;
        c.width = size;
        if (name == tree.displayField) {
            tree.width = size;
            //size = size-1
        }
        tree.root.setWidth(name, size);
    },
    renderLockArea : function() {
        var sf = this,v = 0;
        Ext.each(sf.columns,function(c){
            if (c.lock === true && c.hidden !== true) {
                v += c.width;
            }
        });
        sf.lockWidth = v;
    },
    focusRow : function(row){
        var sf = this,record = sf.dataset.getAt(row),
            node = sf.unlockTree.getNodeById(record.id),
            els = node?node.els:null;
        if(!els)return;
        var ub = sf.ub,
            stop = ub.getScroll().top,
            height = Ext.fly(els.element).getTop()-ub.getTop()+stop;
            r = 25,
            h = ub.getHeight(),
            sh = ub.dom.scrollWidth > ub.dom.clientWidth? 16 : 0;
        if(height<stop){
            ub.scrollTo('top',height-1)
        }else if(height+r>(stop+h-sh)){
            ub.scrollTo('top', height+r-h + sh);
        }
    },
    onUpdate : function(ds,record, name, value){
        $S.TreeGrid.superclass.onUpdate.call(this,ds,record, name, value);
//        var sf = this,c = ((sf.selectable && sf.lockColumns.length == 1) || sf.lockColumns.length == 0)?sf.unlockTree:sf.lockTree,
//            node = c.getNodeById(record.id),
//            tree = node.getOwnerTree();
//            node.els && node.doSetWidth(tree.displayField, tree.getColumn(tree.displayField).width);
    },
    onMouseWheel : function(e){
    },
    onAdd : function(){}
    
});
$S.Tree.TreeGridNode = Ext.extend($S.Tree.TreeNode, {
            createNode : function(item) {
                return new $S.Tree.TreeGridNode(item);
            },
            createCellEl : function(df) {
                var sf = this,tree = sf.getOwnerTree(),tc = tree.columns[0],
                    align = tc.align,r = sf.record, td = sf.els[df + '_td'],
                    text = r.get(tree.displayField)
                    if(!Ext.isEmpty(tree.renderer)){
                        var renderer = window[tree.renderer];
                        if(renderer)
                        text = renderer.call(this, text, r, this);
                    }
//                    sf.els[df + '_td'].innerHTML = text;
                sf.els[df + '_td'] = Ext.DomHelper.insertHtml("afterBegin", td, '<div>'+text+'</div>');
            },
            initColumns : function(){
                var tree = this.ownerTree,w = 0,tr = this.els['itemNodeTr'];
                if (this.isRoot() && tree.showRoot == false) return;
                Ext.fly(tr).set({height:25});
                Ext.each(tree.columns,function(c){
                    var name = c.name,r = this.record;
                    w+=c.width;
                    if(name == tree.displayField && tree.showSkeleton){
                        this.doSetWidth(name,c.width);
                    } else {
                        
                        var td = tr.insertCell(-1),align = c.align;
                        this.els[name + '_td'] = td;
                        if (align) td.style.textAlign = align;
                        td.className = tree.treeGrid.cellCls;
                        Ext.fly(td).set({
                            id:tree.grid.id+'_'+name+'_'+r.id,
                            rid:r.id,
                            width:c.width,
                            stype:'cell',
                            dataindex:name
                        })
                        td.innerHTML = '<div class="tg-cell">' + tree.grid.createCell(c, r,false) + '</div>';
//                        td.innerHTML = '<div style="width:'+(c.width-6)+'px;">' + tree.grid.createCell(c, r,false) + '</div>';
//                        tr.appendChild(td);
//                        Ext.fly(td).addClass(tree.treeGrid.cellCls).setStyle('width',(c.width-6)+'px');
                        Ext.fly(td).addClass(tree.treeGrid.cellCls);//.setStyle('width',(c.width-1)+'px');
//                        Ext.fly(td).setWidth(c.width);
                    }
                },this);
                this.width = w;
                Ext.fly(this.els['itemNodeTable']).setWidth(w)                
            },
            syncWidth:function(){},
            setWidth : function(name,w){
                this.doSetWidth(name,w);
                if(this.childrenRendered) {
                    var pathNodes = this.childNodes;
                    for(var i=0;i<pathNodes.length;i++){
                        var node = pathNodes[i];
                        node.setWidth(name,w);
                    }
                }
                var w = 0;
                Ext.each(this.ownerTree.columns,function(c){
                    w+=c.width;
                },this)
                this.width = w;
                Ext.fly(this.els['itemNodeTable']).setWidth(w)
            },
            doSetWidth : function(name,w){
                if(!w)return;
                if(this.isRoot() && this.getOwnerTree().showRoot == false) return;
                var els = this.els,tree = this.getOwnerTree(),
                    left = w-(name == tree.displayField && tree.showSkeleton ? ((tree.showCheckBox ? 1 : 0) +this.getPathNodes().length)*tree.sw+1 : 0); 
                    Ext.fly(els[name+'_td']).setWidth(left);
                    if(name != tree.displayField ){
                        Ext.fly(els[name+'_td']).first().setWidth(left);
                    }
            }
        });
